一、测试概述
主要目的是测试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());}
}