Spring Boot + security + jwt 测试安全策略

一、测试概述

  主要目的是测试security的用法。因测试搭建mysql和redis比较麻烦,所以我这里将自定义的jwt和用户信息缓存到程序的内存中。
本人测试的项目比较混乱,Spring Boot父类只标出有用的依赖。其子类用的版本为jdk11。后续会继续深入oauth2,敬请期待。
代码地址:https://gitcode.net/qq_40539437/cloud.git
如果想使用自定义jwt工具类往redis里存储请查看源码cloud-jwt模块。

整体代码结构:
在这里插入图片描述

二、maven 相关依赖

1、父类maven相关依赖

 <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><junit.version>4.12</junit.version><log4j.version>1.2.17</log4j.version><lombok.version>1.16.18</lombok.version><mysql.version>5.1.47</mysql.version><druid.version>1.1.16</druid.version><spring.boot.version>2.2.5.RELEASE</spring.boot.version><spring.cloud.version>Hoxton.SR3</spring.cloud.version><spring.cloud.alibaba.version>2.2.1.RELEASE</spring.cloud.alibaba.version> <!-- 版本丛2.1.0.RELEASE升到2.2.1.RELEASE 解决nacos 域名访问问题 --><mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version></properties><dependencyManagement><dependencies><!--springboot 2.2.5--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring.boot.version}</version><type>pom</type><scope>import</scope></dependency><!--Spring cloud Hoxton.SR1--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring.cloud.version}</version><type>pom</type><scope>import</scope></dependency><!--Spring cloud alibaba 2.1.0.RELEASE--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring.cloud.alibaba.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><fork>true</fork><addResources>true</addResources></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build>

2、子类相关依赖

 <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>10</source><target>10</target></configuration></plugin></plugins></build><dependencies><dependency><groupId>com.atguigu.cloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.2</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.2</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.2</version><scope>runtime</scope></dependency><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

二、代码块

1、配置文件 application.yaml

server:port: 20000

2、启动类

package com.atguigu.cloud;import com.atguigu.cloud.cache.MyLocaleCache;
import com.atguigu.cloud.utils.RandomStr;
import com.atguigu.cloud.utils.RsaLocaleUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import java.util.Random;@SpringBootApplication
@EnableWebSecurity
public class CsSecurityTestApplication {public static void main(String[] args) throws Exception {SpringApplication.run(CsSecurityTestApplication.class, args);key();}public static void key() throws Exception {Random random = new Random();int i = random.nextInt(60);String randomString = RandomStr.getRandomString(i);System.err.println("公 私钥随机串: " + randomString);// 生成密钥对RsaLocaleUtils.generateKey(MyLocaleCache.publicKey_S, MyLocaleCache.privateKey_S, randomString, 2048);}}

3、本地缓存

package com.atguigu.cloud.cache;import com.atguigu.cloud.domain.UserInfo;import java.util.HashMap;
import java.util.Map;public class MyLocaleCache {public static String privateKey_S = "privateKey";public static String publicKey_S = "publicKey";private static Map<Long, UserInfo> cache = new HashMap<>();public static UserInfo get(Long key){return cache.get(key);}public static UserInfo set(Long key, UserInfo userInfo){return cache.put(key,userInfo);}
}

4.配置类

package com.atguigu.cloud.conf;import com.atguigu.cloud.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
public class SpringSecurityConfigurer extends WebSecurityConfigurerAdapter {@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;/*** 密码加密方式 相同密码每次加密方式不同,但是每次都能与之匹配*/@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}/** 配置Spring Security 的拦截规则*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/login1").anonymous().antMatchers("/test/**").hasRole("ADMIN") //需要具有 "ADMIN" 角色的用户访问 "/test/**".anyRequest().authenticated(); // 其他路径需要认证http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);}/** 权限管理器*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** {noop} 指代的是当前密码以明文输入  此处测试是指使用security自带的验证页面,设置密码,当实现UserDetailsService之后就不需要进行配置了* @param auth* @throws Exception@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("manager").password("{noop}manager").roles("manager").and().withUser("admin").password("{noop}admin").roles("manager", "ADMIN");}*/
}

5.登录及权限验证接口

登录接口 LoginController:
这里不能直接使用login直接当接口,因为会找security里的login接口

package com.atguigu.cloud.controller;import com.atguigu.cloud.cache.MyLocaleCache;
import com.atguigu.cloud.domain.LoginUser;
import com.atguigu.cloud.domain.UserInfo;
import com.atguigu.cloud.utils.JwtUtils;
import com.atguigu.cloud.utils.RsaLocaleUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;@RestController
@RequestMapping("/login1")
public class LoginController {@Autowiredprivate AuthenticationManager authenticationManager;@GetMapping()public Map<String, Object> login(String username, String password) throws Exception {UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username , password);Authentication authentication = authenticationManager.authenticate(authenticationToken);if(authentication == null) {throw new RuntimeException("用户名或密码错误");}LoginUser loginUser = (LoginUser)authentication.getPrincipal();UserInfo userInfo = loginUser.getUser();MyLocaleCache.set(userInfo.getId(),userInfo);Map<String , Long> params = new HashMap<>() ;params.put("userId" , userInfo.getId()) ;PrivateKey privateKey = RsaLocaleUtils.getPrivateKey(MyLocaleCache.privateKey_S);String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 5);// 构建返回数据Map<String , Object> result = new HashMap<>();result.put("token" , token) ;System.out.println(username + "=" + password);return result;}
}

测试接口TestController:

package com.atguigu.cloud.controller;import com.atguigu.cloud.domain.UserInfo;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.security.Principal;
import java.util.HashMap;
import java.util.Map;@Controller
@RequestMapping("/test")
public class TestController {@GetMapping("/hello")@ResponseBodypublic Map<String,Object> hello(){UserInfo userInfo = (UserInfo)SecurityContextHolder.getContext().getAuthentication().getPrincipal();Map<String, Object> map = new HashMap<>();map.put("测试:","hello");map.put("用户:",userInfo.toString());return map;}
}

6.实体类

从写UserDetails:

package com.atguigu.cloud.domain;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;/**** 下方其他认证可根据业务情况重写*/
public class LoginUser implements UserDetails {private UserInfo userInfo;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List list = new ArrayList();list.add(new SimpleGrantedAuthority("ROLE_"+ userInfo.getRole()));return list;}@Overridepublic String getPassword() {return userInfo.getPassword();}@Overridepublic String getUsername() {return userInfo.getUsername();}@Overridepublic boolean isAccountNonExpired() {          // 账号是否没有过期return true;}@Overridepublic boolean isAccountNonLocked() {           // 账号是否没有被锁定return true;}@Overridepublic boolean isCredentialsNonExpired() {      // 账号的凭证是否没有过期return true;}@Overridepublic boolean isEnabled() {                    // 账号是否可用return true;}public LoginUser() {}public LoginUser(UserInfo user) {this.userInfo = user;}public UserInfo getUser() {return userInfo;}public void setUser(UserInfo user) {this.userInfo = user;}
}

载荷Payload:

package com.atguigu.cloud.domain;import java.util.Date;public class Payload<T> {private String id;private T userInfo;private Date expiration;public Payload() {}public Payload(String id, T userInfo, Date expiration) {this.id = id;this.userInfo = userInfo;this.expiration = expiration;}public String getId() {return id;}public void setId(String id) {this.id = id;}public T getUserInfo() {return userInfo;}public void setUserInfo(T userInfo) {this.userInfo = userInfo;}public Date getExpiration() {return expiration;}public void setExpiration(Date expiration) {this.expiration = expiration;}@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("Payload{");sb.append("id='").append(id).append('\'');sb.append(", userInfo=").append(userInfo);sb.append(", expiration=").append(expiration);sb.append('}');return sb.toString();}
}

用户信息UserInfo:

package com.atguigu.cloud.domain;public class UserInfo {private Long id;private String username;private String password;/** 这里多个可以采用分割符分割后续可以尽心解析*/private String role;public UserInfo() {}public UserInfo(Long id, String username, String role) {this.id = id;this.username = username;this.role = role;}public UserInfo(Long id, String username, String password, String role) {this.id = id;this.username = username;this.password = password;this.role = role;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getRole() {return role;}public void setRole(String role) {this.role = role;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("UserInfo{");sb.append("id=").append(id);sb.append(", username='").append(username).append('\'');sb.append(", password='").append(password).append('\'');sb.append(", role='").append(role).append('\'');sb.append('}');return sb.toString();}
}

7.JWT过滤器

package com.atguigu.cloud.filter;import com.atguigu.cloud.cache.MyLocaleCache;
import com.atguigu.cloud.domain.Payload;
import com.atguigu.cloud.domain.UserInfo;
import com.atguigu.cloud.utils.JwtUtils;
import com.atguigu.cloud.utils.RsaLocaleUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 1、从请求头中获取token,如果请求头中不存在token,直接放行即可!由Spring Security的过滤器进行校验!String token = request.getHeader("token");if(token == null || "".equals(token)) {filterChain.doFilter(request , response);return ;}// 2、对token进行解析,取出其中的userIdPayload<UserInfo> info = null;try {info = JwtUtils.getInfoFromToken(token, RsaLocaleUtils.getPublicKey("publicKey"), UserInfo.class);}catch (Exception e) {e.printStackTrace();throw new RuntimeException("token非法") ;}// 3、使用userId从redis中查询对应的LoginUser对象UserInfo userInfo1 = MyLocaleCache.get(info.getUserInfo().getId());if(userInfo1 != null) {List list = new ArrayList<>();list.add(new SimpleGrantedAuthority("ROLE_"+ userInfo1.getRole()));// 4、然后将查询到的LoginUser对象的相关信息封装到UsernamePasswordAuthenticationToken对象中,然后将该对象存储到Security的上下文对象中UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userInfo1, null ,list) ;SecurityContextHolder.getContext().setAuthentication(authenticationToken);}// 5、放行filterChain.doFilter(request , response);}
}

8.业务类

package com.atguigu.cloud.service;import com.atguigu.cloud.domain.LoginUser;
import com.atguigu.cloud.domain.UserInfo;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;@Service
public class UserDetailsServiceImpl implements UserDetailsService {static Map<String, UserInfo> dataBase = new HashMap<>();static {/** 测试管理员用户*/UserInfo userInfo = new UserInfo();userInfo.setId(1L);userInfo.setUsername("admin");userInfo.setPassword(new BCryptPasswordEncoder().encode("admin")); //不加密方式 "{noop}admin"userInfo.setRole("ADMIN");/** 测试普通用户*/UserInfo userInfo2 = new UserInfo();userInfo2.setId(2L);userInfo2.setUsername("manager");userInfo2.setPassword(new BCryptPasswordEncoder().encode("manager")); //不加密方式 "{noop}admin"userInfo2.setRole("MANAGER");dataBase.put("admin",userInfo);dataBase.put("manager",userInfo2);}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 根据用户名查询用户数据UserInfo userInfo = dataBase.get(username);System.out.println(userInfo);// 如果查询不到数据,说明用户名或者密码错误,直接抛出异常if(userInfo == null) {throw new RuntimeException("用户名或者密码错误") ;}// 将查询到的对象转换成Spring Security所需要的UserDetails对象  这里不需要比对密码return new LoginUser(userInfo);}
}

9.工具类

JsonUtils

package com.atguigu.cloud.utils;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;public class JsonUtils {public static final ObjectMapper mapper = new ObjectMapper();private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);public static String toString(Object obj) {if (obj == null) {return null;}if (obj.getClass() == String.class) {return (String) obj;}try {return mapper.writeValueAsString(obj);} catch (JsonProcessingException e) {logger.error("json序列化出错:" + obj, e);return null;}}public static <T> T toBean(String json, Class<T> tClass) {try {return mapper.readValue(json, tClass);} catch (IOException e) {logger.error("json解析出错:" + json, e);return null;}}public static <E> List<E> toList(String json, Class<E> eClass) {try {return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass));} catch (IOException e) {logger.error("json解析出错:" + json, e);return null;}}public static <K, V> Map<K, V> toMap(String json, Class<K> kClass, Class<V> vClass) {try {return mapper.readValue(json, mapper.getTypeFactory().constructMapType(Map.class, kClass, vClass));} catch (IOException e) {logger.error("json解析出错:" + json, e);return null;}}public static <T> T nativeRead(String json, TypeReference<T> type) {try {return mapper.readValue(json, type);} catch (IOException e) {logger.error("json解析出错:" + json, e);return null;}}
}

JwtUtils

package com.atguigu.cloud.utils;import com.atguigu.cloud.domain.Payload;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.UUID;public class JwtUtils {private static final String JWT_PAYLOAD_USER_KEY = "user";/*** 私钥加密token** @param userInfo   载荷中的数据* @param privateKey 私钥* @param expire     过期时间,单位分钟* @return JWT*/public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {return Jwts.builder().claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo)).setId(createJTI()).setExpiration(DateTime.now().plusMinutes(expire).toDate()).signWith(privateKey, SignatureAlgorithm.RS256).compact();}/*** 私钥加密token** @param userInfo   载荷中的数据* @param privateKey 私钥* @param expire     过期时间,单位秒* @return JWT*/public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {return Jwts.builder().claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo)).setId(createJTI()).setExpiration(DateTime.now().plusSeconds(expire).toDate()).signWith(privateKey, SignatureAlgorithm.RS256).compact();}/*** 公钥解析token** @param token     用户请求中的token* @param publicKey 公钥* @return Jws<Claims>*/private static Jws<Claims> parserToken(String token, PublicKey publicKey) {return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);}private static String createJTI() {return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));}/*** 获取token中的用户信息** @param token     用户请求中的令牌* @param publicKey 公钥* @return 用户信息*/public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {Jws<Claims> claimsJws = parserToken(token, publicKey);Claims body = claimsJws.getBody();Payload<T> claims = new Payload<>();claims.setId(body.getId());claims.setUserInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));claims.setExpiration(body.getExpiration());return claims;}/*** 获取token中的载荷信息** @param token     用户请求中的令牌* @param publicKey 公钥* @return 用户信息*/public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {Jws<Claims> claimsJws = parserToken(token, publicKey);Claims body = claimsJws.getBody();Payload<T> claims = new Payload<>();claims.setId(body.getId());claims.setExpiration(body.getExpiration());return claims;}public static void main(String[] args) {}
}

随机加密工具类:

package com.atguigu.cloud.utils;import java.util.Random;public class RandomStr {//length用户要求产生字符串的长度public static String getRandomString(int length) {String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(62);sb.append(str.charAt(number));}return sb.toString();}
}

RsaLocaleUtils

package com.atguigu.cloud.utils;import org.springframework.stereotype.Component;import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;@Component
public class RsaLocaleUtils {private static final int DEFAULT_KEY_SIZE = 2048;private static ConcurrentHashMap<String,String> cache = new ConcurrentHashMap<>();public static void setCache(String key,String value){cache.put(key, value);}public static String getCache(String key){return cache.get(key);}/***  @Author: CS*  @Description: 从本地缓存中获取  可以修改为redis*/public static void generateKey(String publicKey, String privateKey, String secret, int keySize) throws Exception {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");SecureRandom secureRandom = new SecureRandom(secret.getBytes());keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);KeyPair keyPair = keyPairGenerator.genKeyPair();// 获取公钥并写出byte[] publicKeyBytes = keyPair.getPublic().getEncoded();setCache(publicKey,Base64.getEncoder().encodeToString(publicKeyBytes));// 获取私钥并写出byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();setCache(privateKey,Base64.getEncoder().encodeToString(privateKeyBytes));}/*** 从Redis中 读取密钥** @param privateKey 私钥保存的Key* @return 私钥对象* @throws Exception*/public static PrivateKey getPrivateKey(String privateKey) throws Exception {privateKey = getCache(privateKey).toString();byte[] bytes  = Base64.getDecoder().decode(privateKey);PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);KeyFactory factory = KeyFactory.getInstance("RSA");return factory.generatePrivate(spec);}/*** 从文件中 获取公钥** @param publicKey 公钥的字节形式* @return* @throws Exception*/public static PublicKey getPublicKey(String publicKey) throws Exception {publicKey = getCache(publicKey).toString();byte[] bytes = Base64.getDecoder().decode(publicKey);System.out.println(bytes.length);X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);KeyFactory factory = KeyFactory.getInstance("RSA");return factory.generatePublic(spec);}
}

RsaUtils

package com.atguigu.cloud.utils;import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;//import io.jsonwebtoken.security.Keys;public class RsaUtils {private static final int DEFAULT_KEY_SIZE = 2048;/*** 从文件中读取公钥** @param filename 公钥保存路径,相对于classpath* @return 公钥对象* @throws Exception*/public static PublicKey getPublicKey(String filename) throws Exception {byte[] bytes = readFile(filename);return getPublicKey(bytes);}/*** 从文件中读取密钥** @param filename 私钥保存路径,相对于classpath* @return 私钥对象* @throws Exception*/public static PrivateKey getPrivateKey(String filename) throws Exception {byte[] bytes = readFile(filename);return getPrivateKey(bytes);}/*** 从文件中 获取公钥** @param bytes 公钥的字节形式* @return* @throws Exception*/private static PublicKey getPublicKey(byte[] bytes) throws Exception {bytes = Base64.getDecoder().decode(bytes);X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);KeyFactory factory = KeyFactory.getInstance("RSA");return factory.generatePublic(spec);}/*** 获取密钥** @param bytes 私钥的字节形式* @return* @throws Exception*/private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {bytes = Base64.getDecoder().decode(bytes);PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);KeyFactory factory = KeyFactory.getInstance("RSA");return factory.generatePrivate(spec);}/*** 根据密文,生存rsa公钥和私钥,并写入指定文件** @param publicKeyFilename  公钥文件路径* @param privateKeyFilename 私钥文件路径* @param secret             生成密钥的密文*/public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");SecureRandom secureRandom = new SecureRandom(secret.getBytes());keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);KeyPair keyPair = keyPairGenerator.genKeyPair();// 获取公钥并写出byte[] publicKeyBytes = keyPair.getPublic().getEncoded();publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);writeFile(publicKeyFilename, publicKeyBytes);// 获取私钥并写出byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);writeFile(privateKeyFilename, privateKeyBytes);}private static byte[] readFile(String fileName) throws Exception {return Files.readAllBytes(new File(fileName).toPath());}private static void writeFile(String destPath, byte[] bytes) throws IOException {File dest = new File(destPath);if (!dest.exists()) {dest.createNewFile();}Files.write(dest.toPath(), bytes);}public static void testfor() {KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);PrivateKey aPrivate = keyPair.getPrivate();PublicKey aPublic = keyPair.getPublic();}
}

10.测试类

package com.cloud.atguigu.jwt;import com.atguigu.cloud.domain.Payload;
import com.atguigu.cloud.domain.UserInfo;
import com.atguigu.cloud.utils.JwtUtils;
import com.atguigu.cloud.utils.RandomStr;
import com.atguigu.cloud.utils.RsaLocaleUtils;
import org.junit.Test;import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Random;public class JwtTest {private  String privateKey_S = "privateKey";private String publicKey_S = "publicKey";@Testpublic void testRSAByLocale() throws Exception {Random random = new Random();int i = random.nextInt(60);String randomString = RandomStr.getRandomString(i);randomString = "cs";System.err.println(randomString);// 生成密钥对RsaLocaleUtils.generateKey(publicKey_S, privateKey_S, randomString, 2048);// 获取私钥PrivateKey privateKey = RsaLocaleUtils.getPrivateKey(privateKey_S);System.out.println("privateKey = " + privateKey.toString());PublicKey publicKey = RsaLocaleUtils.getPublicKey(publicKey_S);System.out.println("publicKey = " + publicKey.toString());}@Testpublic void testJWTByLocale() throws Exception {Random random = new Random();int i = random.nextInt(60);String randomString = RandomStr.getRandomString(i);randomString = "cs";System.err.println(randomString);// 生成密钥对RsaLocaleUtils.generateKey(publicKey_S, privateKey_S, randomString, 2048);// 获取私钥PrivateKey privateKey = RsaLocaleUtils.getPrivateKey(privateKey_S);// 生成tokenString token = JwtUtils.generateTokenExpireInMinutes(new UserInfo(1L, "Jack", "guest"), privateKey, 5);System.out.println("token = " + token);System.err.println("privateKey:" + privateKey.toString());// 获取公钥PublicKey publicKey = RsaLocaleUtils.getPublicKey(publicKey_S);// 解析tokenPayload<UserInfo> info = JwtUtils.getInfoFromToken(token, publicKey, UserInfo.class);System.out.println("info.getExpiration() = " + info.getExpiration());System.out.println("info.getUserInfo() = " + info.getUserInfo());System.out.println("info.getId() = " + info.getId());}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/657089.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库之TiDB基础讲解

文章目录 1 TiDB1.1 引言1.2 TiDB介绍1.3 系统架构1.3.1 TIDB Server1.3.2 PD Server1.3.3 TIKV Server1.3.4 TiKV如何不丢失数据1.3.5 分布式事务支持 1.4 与MySQL的对比1.5 性能测试1.5.1 测试一1.5.2 系统测试报告 2 1 TiDB 1.1 引言 当我们使用 Mysql 数据库到达一定量级…

调试小结:PHY初始化前后Link Status是否能正确反应网线插上、拔下状态

1 说明 为了验证是否需要初始化PHY才能检测到网线插上、拔下&#xff0c;这里我们对比初始化PHY&#xff08;LAN8720&#xff09;前后&#xff0c;插拔网线PHY寄存器1的bit2的是否按照预期变化来进行测试。 我们查看的PHY寄存器是1的bit2&#xff0c;定义如下&#xff1a; 2…

springboot的actuator

1、actuator简介 微服务的特点决定了功能模块的部署是分布式的&#xff0c;大部分功能模块都是运行在不同的机器上&#xff0c;彼此通过服务调用进行交互&#xff0c;前后台的业务流会经过很多个微服务的处理和传递&#xff0c;出现了异常如何快速定位是哪个环节出现了问题&am…

STM32 CAN接口中断处理与性能优化技巧

在基于STM32的CAN接口中&#xff0c;中断处理是一个非常重要的部分&#xff0c;它可以帮助我们实时地处理接收到的数据和处理其他CAN事件。为了优化CAN接口的性能&#xff0c;以下是一些中断处理和性能优化的技巧&#xff1a; ✅作者简介&#xff1a;热爱科研的嵌入式开发者&am…

系统架构19 - 面向对象

面向对象设计 相关概念面向对象分析基本步骤基本原则分析模型 面向对象设计设计模型类的类型 面向对象编程基本特点需求建模设计原则面向对象软件测试 相关概念 接口&#xff1a;描述对操作规范的说明&#xff0c;其只说明操作应该做什么&#xff0c;并没有定义操作如何做。消…

C与C++相互调用的“底层原理“

在你的C语言代码中&#xff0c;不知能否看到类似下面的代码&#xff1a; 这好像没有什么问题&#xff0c;你应该还会想&#xff1a;“嗯⋯是啊&#xff0c;我们的代码都是这样写的&#xff0c;从来没有因此碰到过什么麻烦啊&#xff5e;”。 你说的没错&#xff0c;如果你的头…

ES6理论及方法

一、基础知识 1.不可枚举就是不可遍历。 2.数据代理&#xff1a;通过一个对象代理对另一个对象中属性的操作&#xff08;读/写&#xff09; 二、方法 1.一个字符串是否包含在另一个字符串中 includes()&#xff1a;返回布尔值&#xff0c;表示是否找到了参数字符串。 starts…

Excel中将16进制数转化成10进制(有/无符号)

Excel中将16进制数转化成10进制&#xff08;有/无符号&#xff09; Excel或者matlab中常用XXX2XXX进行不同进制的转换 16进制转10进制&#xff08;无符号数&#xff09;&#xff1a;HEX2DEC 16进制转10进制&#xff08;有符号数&#xff09;&#xff1a; FA46为例&#xff0c…

看到小米SU7这顶配价格 全都坐不住了

文 | AUTO芯球 作者 | 李诞 果然被我猜中了吗&#xff1f; 我是真的猜中了吗 之前我的视频里说 小米SU7这技术、这车 绝对值35万 今天爆出来 小米汽车上牌售价 保险费用显示顶配36.14万 难怪雷总说 这是按照Dream Car的标准来设计的 投入百亿 雷总这点是真没骗人 …

架构整洁之道-价值维度与编程范式

1 设计与架构究竟是什么 结论&#xff1a;二者没有任何区别&#xff0c;一丁点区别都没有。 架构图里实际上包含了所有底层设计细节&#xff0c;这些细节信息共同支撑了顶层的架构设计&#xff0c;底层设计信息和顶层架构设计共同组成了整个架构文档。底层设计细节和高层架构信…

Matplotlib应用-股票技术分析实战

MACD Moving Average Convergence/Divergence&#xff0c;意为异同移动平均线。它刻画的是股价变化的速度 MACD算法 指标含义公式短期EMA短期收盘价指数移动均线(12天)前一日EMA(12)11/13 今日收盘价2/13长期EMA长期收盘价指数移动均线(26天)前一日EMA(26)25/27 今日收盘价2…

数据结构——并查集

1.并查集的定义 并查集其实也是一种树形结构&#xff0c;在使用中通常用森林的方式来表示 并查集的逻辑结构其实就是集合 并查集一般可以通过双亲写法&#xff08;顺序结构&#xff09;来完成&#xff0c;即通过一个数组存储父亲结点的下标 int s[10005]; int main() {for(…

深入了解Yum:Linux系统的软件包管理利器

目录 软件包 软件包的来源 关于yum yum是什么 yum的相关操作 介绍rzsz rz&#xff08;从Windows本地传到Linux服务器&#xff09; sz&#xff08;从Linux服务器传到Windows本地&#xff09; 注意事项 查看软件包 安装软件 卸载软件 yum的本地配置 为什么要进行配置…

《基于“源启+”的应用重构白皮书》

当前&#xff0c;行业数字化转型驶入“深水区”&#xff0c;全新的市场竞争格局对行业发展提出更高的要求&#xff0c;企业高质量发展需要借助新架构新应用重新定义数字生产力&#xff0c;重塑商业模式与市场核心竞争力。 在中国电子主办&#xff0c;中电金信承办的“数字原生向…

Git学习笔记(第10章):自建代码托管中心GitLab

目录 10.1 简介 10.2 安装 10.2.1 准备服务器 10.2.2 准备安装包 10.2.3 编写安装脚本 10.2.4 初始化GitLab服务 10.2.5 启动GitLab服务 10.2.6 使用浏览器访问GitLab 10.3 创建远程库 10.4 本地库推送到远程库 10.1 简介 GitLab是由GitLab Inc.开发&#xff0c;使用…

Nacos服务注册源码:客户端

入口 我们就拿nacos自己example下的NamingExample来做测试 public class NamingExample {public static void main(String[] args) throws NacosException, InterruptedException {Properties properties new Properties();properties.setProperty("serverAddr", …

39、FlatFormer:MIT+交大+清华共同提出,更加高效的Transformer[无绳蹦极第一人!]

本文由麻省理工学院、上海交通大学、清华大学于2023年7月14日发表于Arxiv的<Computer Science> 论文&#xff1a; [2301.08739] FlatFormer: Flattened Window Attention for Efficient Point Cloud Transformer (arxiv.org) 代码&#xff1a; github.com Abstract&a…

接续奋斗四十载 勇毅前行向未来 2024食品产业高质量发展论坛暨《中国食品报》创刊40周年研讨会在京举行

四十年春华秋实&#xff0c;四十年砥砺奋进。《中国食品报》从改革开放之初的春天里走来&#xff0c;以笔为媒&#xff0c;勇担道义&#xff0c;从拯救中华传统老字号品牌开始&#xff0c;为我国食品产业发展奔走疾呼&#xff0c;为提升我国食品企业融入国际化潮流、提升国际竞…

联合 Maxlinear 迈凌 与 Elitestek 易灵思 - WPI 世平推出基于 FPGA 芯片的好用高效电源解决方案

近期 WPI 世平公司联合 Maxlinear 迈凌电源产品搭配 Elitestek 易灵思 FPGA 共同合作推出基于 FPGA 芯片的好用高效电源解决方案。 Elitestek 易灵思 FPGA 核心产品有 2 大系列 : Trion 系列与钛金系列。Trion 系列主要特点是 : 1. 40nm 工艺 2. 超低功耗 ( 可低至竞争对手的 …

[ESXi 8]安装centos7

文章目录 创建虚拟机创建虚拟机选择centos7选择存储选择镜像文件上传ios镜像文件 安装即将完成 启动虚拟机自动获取ip设置root密码安装成功 创建虚拟机 创建虚拟机 选择centos7 选择存储 选择镜像文件 上传ios镜像文件 如图显示上传进度&#xff0c;上传完毕之后&#xff0c;将…