JWT相关介绍
介绍
JWT全称为JSON Web Token
。它采用了 JSON
格式来对 Token 进行编码和解码,并携带了更多的信息,例如用户ID、角色、权限等。
JWT共分为三个部分以 .
进行分割。三个部分分别为header(头部)、payload(数据)、signature(签名)
例:
eyJ0eXAiOiJKV1QiLCJjdHkiOiJERVMiLCJ6aXAiOiJIUzI1NiIsImFsZyI6IkhTMjU2In0.
eyJqdGkiOiI5ZTU5NmQ0Zi0wMDNlLTRhZjctOGFkOS1jODU1MmYxY2U2MGIiLCJzdWIiOiLlpJYiLCJleHAiOjE3MzQ5NTI1MTksImlzcyI6IuWGhSIsImlhdCI6MTczNDk1MDcxOSwiYXVkIjoi5o6l5pS25Lq6IiwibmJmIjoxNzM0OTUwNzE4fQ.
snvBg6iY1UgzMFsWS4UeIPYbvB8vfXQkxZ4ul4VM_nU
base64解码后
头部
头部是令牌描述(编码的JSON)一般都不需要我们去手动配置,除非我们对内容进行加密,又想将加密内容的加密告诉前端人员用什么方法解密可以配置cty
参数。当然我们除了下面的几个参数外,还可以自定义一些参数。
typ
:TYPE 表明这个令牌的类型 (即JWT)cty
:CONTENT_TYPE 如果对载荷进行了加密这个字段可用来表明载荷用了哪种加密算法,也可以忽略不填alg
:COMPRESSION_ALGORITHM 令牌采用的压缩签名算法
载荷
数据的有效载荷,一般存储用于描述用户信息、权限、过期时间等等。下面介绍一些预定义的声明(也称为公共声明),除下面这几个公共声明外,我们还可以自定义一些其他的声明,(自定义的声明又称为私有声明)通常用于传递敏感信息或特定于应用的数据
jti
:唯一标识、JWT的唯一身份标识sub
:主题,标识JWT所面向的用户,通常用于验证用户的身份。exp
:到期时间,标识JWT的过期时间,超过这个时间后JWT将不再有效。iss
:签发者,标识JWT的签发者,通常用于验证JWT的来源。iat
:签发时间,标识JWT的签发时间,通常用于记录和验证JWT的生成时间。aud
:接受者,标识接收JWT的实体,确保JWT被正确的实体接收。nbf
:生效时间,标识JWT生效的时间点,在此之前JWT不会被接受。
签名
通过加密算法对头部信息和有效载荷信息进行加密压缩,确保令牌的内容不被修改
使用
代码使用
引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${version}</version>
</dependency>
创建工具类
public class JwtUtil {
private static final String SECRET = "c2VjcmV0";
private static final Long EXPIRATION_TIME = 1800000L;
public static String generateToken(String key) {
return createToken(key);
}
/**
* 私有方法,用于实际生成 JWT
*
* @param subject
* @return
*/
private static String createToken(String subject) {
Claims claims = Jwts.claims()
.setId(UUID.randomUUID().toString())
.setSubject(subject)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.setIssuer("gateway-auth")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setNotBefore(new Date(System.currentTimeMillis()));
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.HS256,SECRET)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串
}
private static String createToken(Claims claims) {
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.HS256,SECRET)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串
}
/**
* 判断令牌是否是系统签发,且是否在使用时间范围内
* @param token 令牌
* @return 校验正常 返回 true,校验失败返回false
*/
public static boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
if (!isTokenTimeValidate(token)) {
return false;
}
} catch (Exception e) {
log.error("令牌校验失败,失败令牌信息为:{}", token);
return false;
}
return true;
}
/**
* 判断令牌是否到了刷新时间,如果到了刷新时间返回新令牌,否则返回旧令牌
* @param token
* @return
*/
public static String refreshToken(String token) {
Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
Date expiration = claims.getExpiration();
long difference = expiration.getTime()-System.currentTimeMillis();
if((double)difference/EXPIRATION_TIME<0.3d){
claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));
return createToken(claims);
}
return token;
}
public static <T> T getClaimParam(String token, String paraName, Class<T> type) {
Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
return claims.get(paraName, type);
}
private static Boolean isTokenTimeValidate(String token) {
Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
if (claims.getNotBefore() != null && !claims.getNotBefore().before(new Date())) {
return false;
}
if (claims.getExpiration() != null && new Date().after(claims.getExpiration())) {
return false;
}
return true;
}
}
创建Jwt拦截器及在SpringSecurity中使用
WebFlux中使用示例
JwtTokenFilter
@Component
public class JWTTokenFilter implements WebFilter {
@Autowired
private RedisUtil<LoginUser> redisUtil;
public static final String LOGIN_USER="LOGIN_USER:";
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
// 获取http协议请求对象
ServerHttpRequest request = exchange.getRequest();
// 获取http协议返回对象
ServerHttpResponse response = exchange.getResponse();
//获取请求头里面的令牌参数
List<String> authorizations = request.getHeaders().get("Authorization");
//校验令牌参数是否存在。不存在则放行到下一个过滤器
if(CollectionUtils.isEmpty(authorizations)){
return chain.filter(exchange);
}
// 获取请求的令牌信息
String authToken = authorizations.get(0);
// 判断令牌是否为空和令牌作用提示信息与要判断的是否一致
if(authToken==null||!authToken.startsWith("Bearer ")){
//放行到下一个过滤器
return chain.filter(exchange);
}
//获取真正的jwtToken信息
String jwtToken = authToken.substring(7);
//校验token是否可信
if(!JwtUtil.validateToken(jwtToken)){
return chain.filter(exchange);
}
//获取token里面的负载信息
String proof = JwtUtil.getClaimParam(jwtToken, Claims.SUBJECT, String.class);
String loginUserRedisKey = LOGIN_USER + proof;
//根据负载信息获取存储redis的登录用户信息
LoginUser loginUserInfo = redisUtil.get(loginUserRedisKey);
if(loginUserInfo==null){
return chain.filter(exchange);
}
//判断token是否到了更新过期时间,如果是则返回新的token,不是则返回原有的token
String newToken = JwtUtil.refreshToken(jwtToken);
//如果新token和旧token不一致则更新redis存储用户信息的键的过期时间
if(!jwtToken.equals(newToken)){
redisUtil.expire(loginUserRedisKey,30, TimeUnit.MINUTES);
}
//将token信息返回给前端,让前端存储或更新
response.getHeaders().add("Authorization","Bearer " + newToken);
//将登录用户信息放入到请求的上下文中,方便后面的服务层获取当前请求的登录人信息
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUserInfo, authToken, loginUserInfo.getAuthorities());
return chain.filter(exchange).contextWrite(ReactiveSecurityContextHolder.withAuthentication(authenticationToken));
}
}
返回对校验异常信息返回进行拦截并返回准确的异常信息,以便前端进行处理
@Slf4j
@Component
public class CustomerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
@Autowired
private RedisUtil<LoginUser> redisUtil;
public static final String LOGIN_USER="LOGIN_USER:";
@Override
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException ex) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
List<String> authorizations = request.getHeaders().get("Authorization");
if(CollectionUtils.isEmpty(authorizations)){
return backAuthFailMsg(response,null);
}
String authToken = authorizations.get(0);
if(authToken==null||!authToken.startsWith("Bearer ")){
return backAuthFailMsg(response,null);
}
String jwtToken = authToken.substring(7);
if(!JwtUtil.validateToken(jwtToken)){
return backAuthFailMsg(response,null);
}
String proof = JwtUtil.getClaimParam(jwtToken, Claims.SUBJECT, String.class);
String loginUserRedisKey = LOGIN_USER + proof;
LoginUser loginUserInfo = redisUtil.get(loginUserRedisKey);
if(loginUserInfo==null){
return backAuthFailMsg(response,"Certificate is invalid, please login again");
}
String newToken = JwtUtil.refreshToken(jwtToken);
if(!jwtToken.equals(newToken)){
redisUtil.expire(loginUserRedisKey,30, TimeUnit.MINUTES);
}
return null;
}
public Mono<Void> backAuthFailMsg(ServerHttpResponse response, String msg){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
String errorMsg = "Authorization header is missing or invalid";
if(msg!=null){
errorMsg = msg;
}
try {
return response.writeWith(Mono.just(response.bufferFactory().wrap(errorMsg.getBytes("UTF-8"))));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
在SpringSecurity中进行配置
@Configuration
@EnableWebFluxSecurity
public class GatewaySecurityConfig {
@Autowired
private CustomerAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private LogoutHandler logoutHandler;
@Autowired
private JWTTokenFilter jwtTokenFilter;
@Autowired
private AuthUserService authUserService;
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// 禁用请求头传输用户名密码登录请求和表单登录请求
http.httpBasic().disable().formLogin().disable();
//允许任何人可以访问请求登录接口,其他接口需要鉴权
http.authorizeExchange().pathMatchers("/auth/login").permitAll().anyExchange().authenticated()
//设置鉴权失败异常信息处理点
.and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
//配置退出登录接口和退出登录操作处理器
.and().logout().logoutUrl("/logout").logoutHandler(logoutHandler);
//将自己配置的jwtToken拦截器替换掉原有的认证拦截器
http.addFilterAt(jwtTokenFilter, SecurityWebFiltersOrder.AUTHENTICATION);
//开启允许跨域和关闭csrf(跨站请求攻击)安全拦截
http.cors().and().csrf().disable();
return http.build();
}
@Bean
public ReactiveAuthenticationManager authenticationManager() {
UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(authUserService);
authenticationManager.setPasswordEncoder(passwordEncoder());
return authenticationManager;
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@Component
public class AuthUserService implements ReactiveUserDetailsService {
@Autowired
private UserService userService;
@Override
public Mono<UserDetails> findByUsername(String username) {
return Mono.justOrEmpty(userService.loadUserByUsername(username));
}
}
登录接口代码
@Slf4j
@Service
public class LocalAuthServiceImpl implements AuthService {
public static final String INIT_USERNAME="admin";
public static final String INIT_PASSWORD="123456";
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserService userService;
@Autowired
private ReactiveAuthenticationManager authorizationManager;
@Autowired
private AuthenticationSuccessHandler successHandler;
@Autowired
private AuthenticationFailureHandler failureHandler;
@Override
public Mono<Void> authenticate(String username, String password, ServerWebExchange exchange) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
username,
password
);
return authorizationManager.authenticate(authenticationToken)
.flatMap(authentication -> successHandler.onAuthenticationSuccess(exchange, authentication))
.onErrorResume(exception -> failureHandler.onAuthenticationFailure(exchange, exception));
}
@Override
public synchronized void initUser(){
LoginUser loginUser = userService.loadUserByUsername(INIT_USERNAME);
if(loginUser!=null) {
return;
}
LocalDateTime maxExpireTime = LocalDateTime.parse("9999-12-31T23:59:59");
loginUser = new LoginUser(INIT_USERNAME,passwordEncoder.encode(INIT_PASSWORD),"初始化管理员","M",maxExpireTime);
userService.addUser(loginUser.getUser());
}
}
@Slf4j
@Component
public class AuthenticationFailureHandler {
public Mono<Void> onAuthenticationFailure(ServerWebExchange exchange, Throwable exception) {
ServerHttpResponse response = exchange.getResponse();
if (log.isWarnEnabled()) {
log.warn("User {} failed authentication",exchange.getRequest().getQueryParams().getFirst("username"));
}
HttpHeaders headers = response.getHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, "application/json; charset=utf-8");
response.setStatusCode(HttpStatus.PRECONDITION_FAILED);
String jsonString = JSON.toJSONString(Result.failure(HttpStatus.PRECONDITION_FAILED,exception.getMessage()));
DataBuffer dataBuffer = null;
try {
dataBuffer = response.bufferFactory().wrap(jsonString.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return response.writeWith(Mono.just(dataBuffer));
}
}
@Slf4j
@Component
public class AuthenticationSuccessHandler {
public static final String LOGIN_USER="LOGIN_USER:";
@Autowired
private RedisUtil<LoginUser> loginUserRedisUtil;
public Mono<Void> onAuthenticationSuccess(ServerWebExchange exchange, Authentication authentication) {
ServerHttpResponse response = exchange.getResponse();
String simpleUUID = UUID.randomUUID().toString().replace("-", "");
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
loginUserRedisUtil.set(LOGIN_USER+simpleUUID, loginUser,30, TimeUnit.MINUTES);
String token = JwtUtil.generateToken(simpleUUID);
response.getHeaders().add("Authorization", "Bearer " + token);
log.info("User {} login success", authentication.getName());
response.setStatusCode(HttpStatus.ACCEPTED);
HttpHeaders headers = response.getHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
String responseJson = JSON.toJSONString(Result.success());
DataBuffer dataBuffer = null;
try {
dataBuffer = response.bufferFactory().wrap(responseJson.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return response.writeWith(Mono.just(dataBuffer)) ;
}
}
public interface AuthService {
Mono<Void> authenticate(String username, String password, ServerWebExchange exchange);
}
@Slf4j
@Service
public class LocalAuthServiceImpl implements AuthService {
public static final String INIT_USERNAME="admin";
public static final String INIT_PASSWORD="123456";
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserService userService;
@Autowired
private ReactiveAuthenticationManager authorizationManager;
@Autowired
private AuthenticationSuccessHandler successHandler;
@Autowired
private AuthenticationFailureHandler failureHandler;
@Override
public Mono<Void> authenticate(String username, String password, ServerWebExchange exchange) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
username,
password
);
return authorizationManager.authenticate(authenticationToken)
.flatMap(authentication -> successHandler.onAuthenticationSuccess(exchange, authentication))
.onErrorResume(exception -> failureHandler.onAuthenticationFailure(exchange, exception));
}
}
退出登录的示例代码这里就不在写了直接删除redis的登录用户信息即可,并让前端也删除保存的token
HttpWeb使用示例
下面的代码只写基础框架,不写具体逻辑
JwtTokenFilter
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//从请求头获取token信息并进行校验
//...
//校验逻辑
//...
//校验结束并获取当前登录人的信息
//当前登录用户信息及权限封装SpringSecurity的上下文中方便后面获取登录人的信息
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
log.debug("authenticated user:{}", account);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 放行到下一个过滤器
filterChain.doFilter(request, response);
}
}
返回对校验异常信息返回进行拦截并返回准确的异常信息,以便前端进行处理
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -8970718410437077606L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setHeader("content-type","application/json;charset=UTF-8");
String msg = "请求访问:"+request.getRequestURI()+",认证失败,无法访问系统资源";
ResultBean<String> error = ResultBean.error(401,"fail",msg);
response.getWriter().write(JSON.toJSONString(error));
}
}
SpringSecurity配置
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Lazy
@Autowired
private UserServiceImpl userServiceImpl;
@Autowired
private PasswordEncoder passwordEncoder;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
if(enabled) {
//定义哪些URL需要被保护、定义哪些不需要被保护
http.authorizeRequests()
//设置未登录用户可以访问的页面
.antMatchers("/auth/login").anonymous()
// 任何请求,登录后可以访问
.anyRequest().authenticated()
//禁止session认证
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//设置登录异常处理类
.and().exceptionHandling().authenticationEntryPoint(unauthorizedHandler())
.and().csrf().disable(); // 关闭csrf防护
http.cors(); // 设置允许跨域
http.logout().logoutUrl("/api/power/v4/user/logout").logoutSuccessHandler(customerLogoutSuccessHandler());
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userServiceImpl).passwordEncoder(passwordEncoder);
}
@Bean
public AuthenticationEntryPointImpl unauthorizedHandler() {
return new AuthenticationEntryPointImpl();
}
}
登录接口代码
@PostMapping("/login")
@ApiOperation("用户登录接口")
public ResultBean<String> login(@RequestBody UserForm userForm, HttpServletResponse response){
return ResultBean.success(userService.login(userForm,response));
}
@Lazy
@Autowired
private AuthenticationManager authenticationManager;
public String login(UserForm userForm, HttpServletResponse response) {
String username = userForm.getUsername();
String password = userForm.getPassword();
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
redisTemplate.opsForHash().put("LOGIN_USER_INFO",loginUser.getUser().getIuid(),loginUser);
User user = loginUser.getUser();
DefaultClaims claims = new DefaultClaims();
claims.setSubject(user.getAccount());
String token = jwtTokenUtil.generateToken(claims);
response.setHeader(tokenHeader,token);
return loginUser.getUser().getIuid();
}
签名算法介绍及使用示例
HS签名算法
HS256(HMAC using SHA-256)是一种基于哈希函数SHA-256和密钥的消息认证码(HMAC)算法。使用相同的秘钥来进行签名和验证。所以它b不是一种对称加密算法,而是一种用于验证消息完整性和身份验证的算法。
特点
基于哈希:HS256使用SHA256哈希算法来生成签名
对称秘钥:HS256使用相同的秘钥来签名和验证JWT。这意味着双方必须共享相同的秘钥
安全性:由于秘钥的私密性,只有拥有相同密钥的一方才能验证JWT的真实性。
应用场景:通常用于客户端和服务器之间的认证和授权过程,特别是无状态的应用程序。
使用示例
@Slf4j
public class JwtUtil {
// 配置签名验证秘钥
private static final String SECRET = "c2VjcdmV0";
private static final Long EXPIRATION_TIME = 1800000L;
public static String generateToken(String key) {
return createToken(key);
}
/**
* 私有方法,用于实际生成 JWT
*
* @param subject
* @return
*/
private static String createToken(String subject) {
Claims claims = Jwts.claims()
.setId(UUID.randomUUID().toString())
.setSubject(subject)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.setIssuer("gateway-auth")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setNotBefore(new Date(System.currentTimeMillis()));
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.HS256,SECRET)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串
}
private static String createToken(Claims claims) {
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.HS256,SECRET)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串
}
/**
* 判断令牌是否是系统签发,且是否在使用时间范围内
* @param token 令牌
* @return 校验正常 返回 true,校验失败返回false
*/
public static boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
if (!isTokenTimeValidate(token)) {
return false;
}
} catch (Exception e) {
log.error("令牌校验失败,失败令牌信息为:{}", token);
return false;
}
return true;
}
//测试
public static void main(String[] args) {
String token = JwtUtil.generateToken("123456");
System.out.println(token);
System.out.println(JwtUtil.validateToken(token));
}
//结果输出
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJhNGViMGRjYy0xZDdkLTQ3MDMtODJiMC1kZDY2NGQ1MDYyOTUiLCJzdWIiOiIxMjM0NTYiLCJleHAiOjE3MzU4OTIyODksImlzcyI6ImdhdGV3YXktYXV0aCIsImlhdCI6MTczNTg5MDQ4OSwibmJmIjoxNzM1ODkwNDg5fQ.dE4GG1NIzLYsC7gov02JvqTzhQClwWAJ6Ti458AQnM8
true
}
HS256、HS384、HS512的区别
HS384、HS512与HS256的主要区别在于它们使用的哈希算法不同,具体如下:
哈希算法不同:
HS256:使用SHA-256算法进行HMAC签名。
HS384:使用SHA-384算法进行HMAC签名。
HS512:使用SHA-512算法进行HMAC签名。
安全性:
HS256:虽然使用较短的哈希值,但在大多数情况下足够安全,适用于信任的系统内部,如单体应用或集中式认证服务。
HS384和HS512:由于使用更长的哈希值,提供了更高的安全性,适用于需要更高安全性的场景。
性能:
HS256:由于使用较短的哈希值,计算速度较快。
HS384和HS512:由于哈希值较长,计算速度相对较慢,但提供了更高的安全性。
应用场景:
HS256:适用于信任的系统内部,如单体应用或集中式认证服务。
HS384和HS512:适用于需要更高安全性的分布式系统和多服务环境。
RS签名算法
特点:这些算法使用非对称加密,即有一对公钥和私钥。私钥用于签名,公钥用于验证。即使公钥被泄露,只要私钥保持安全,Token依然是安全的。适用于分布式系统和多服务环境。
public class RsaKeyUtil {
/**
* 生成RSA密钥对
*
* @param keySize 密钥长度(例如2048)
* @return 生成的KeyPair对象
* @throws NoSuchAlgorithmException 如果指定的算法不存在
*/
public static KeyPair generateKeyPair(int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(keySize);
return keyGen.generateKeyPair();
}
/**
* 将私钥保存到文件
*
* @param privateKey 私钥
* @param filename 文件路径
* @throws Exception 如果发生错误
*/
public static void savePrivateKey(PrivateKey privateKey, String filename) throws Exception {
Path path = Paths.get(filename);
if (Files.notExists(path.getParent())) {
Files.createDirectories(path.getParent());
}
if(Files.notExists(path)){
Files.createFile(path);
}
byte[] encodedKey = privateKey.getEncoded();
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(encodedKey);
String pemString = "-----BEGIN PRIVATE KEY-----\n" +
Base64.getEncoder().encodeToString(pkcs8KeySpec.getEncoded()) +
"\n-----END PRIVATE KEY-----";
Files.write(path, pemString.getBytes());
}
/**
* 将公钥保存到文件
*
* @param publicKey 公钥
* @param filename 文件路径
* @throws Exception 如果发生错误
*/
public static void savePublicKey(PublicKey publicKey, String filename) throws Exception {
Path path = Paths.get(filename);
// 确保父目录存在
if (Files.notExists(path.getParent())) {
Files.createDirectories(path.getParent());
}
if(Files.notExists(path)){
Files.createFile(path);
}
byte[] encodedKey = publicKey.getEncoded();
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(encodedKey);
String pemString = "-----BEGIN PUBLIC KEY-----\n" +
Base64.getEncoder().encodeToString(x509KeySpec.getEncoded()) +
"\n-----END PUBLIC KEY-----";
Files.write(path, pemString.getBytes());
}
}
@Slf4j
public class JwtRsaUtil {
private static final String RSA_PRIVATE_KEY_PATH="security/jwt/private.key";
private static final String RSA_PUBLIC_KEY_PATH="security/jwt/public.key";
private static final Long EXPIRATION_TIME = 1800000L;
public static String generateToken(String key) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return createToken(key);
}
private static PrivateKey getRsaPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Path path = Paths.get(RSA_PRIVATE_KEY_PATH);
if(Files.notExists(path)){
throw new FileNotFoundException("私钥文件不存在");
}
byte[] bytes = Files.readAllBytes(path);
String privateKeyString = new String(bytes, StandardCharsets.UTF_8);
String privateKeyPem = privateKeyString.substring(28, privateKeyString.length() - 26);
byte[] decoded = Base64.getDecoder().decode(privateKeyPem);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
private static PublicKey getRsaPublicKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Path path = Paths.get(RSA_PUBLIC_KEY_PATH);
if(Files.notExists(path)){
throw new FileNotFoundException("公钥文件不存在");
}
byte[] bytes = Files.readAllBytes(path);
String publicKeyString = new String(bytes, StandardCharsets.UTF_8);
String publicKeyPem = publicKeyString.substring(27, publicKeyString.length() - 25);
byte[] decoded = Base64.getDecoder().decode(publicKeyPem);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
/**
* 私有方法,用于实际生成 JWT
*
* @param subject
* @return
*/
private static String createToken(String subject) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
Claims claims = Jwts.claims()
.setId(UUID.randomUUID().toString())
.setSubject(subject)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.setIssuer("gateway-auth")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setNotBefore(new Date(System.currentTimeMillis()));
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.RS256, privateKey)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串
}
private static String createToken(Claims claims) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.RS256,privateKey)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串.
}
/**
* 判断令牌是否是系统签发,且是否在使用时间范围内
* @param token 令牌
* @return 校验正常 返回 true,校验失败返回false
*/
public static boolean validateToken(String token) {
try {
PrivateKey privateKey = getRsaPrivateKey();
Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token);
if (!isTokenTimeValidate(token)) {
return false;
}
} catch (Exception e) {
log.error("令牌校验失败,失败令牌信息为:{}", token);
return false;
}
return true;
}
/**
* 判断令牌是否到了刷新时间,如果到了刷新时间返回新令牌,否则返回旧令牌
* @param token
* @return
*/
public static String refreshToken(String token) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
Claims claims = Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token).getBody();
Date expiration = claims.getExpiration();
long difference = expiration.getTime()-System.currentTimeMillis();
if((double)difference/EXPIRATION_TIME<0.3d){
claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));
return createToken(claims);
}
return token;
}
public static <T> T getClaimParam(String token, String paraName, Class<T> type) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
Claims claims = Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token).getBody();
return claims.get(paraName, type);
}
private static Boolean isTokenTimeValidate(String token) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
Claims claims = Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token).getBody();
if (claims.getNotBefore() != null && !claims.getNotBefore().before(new Date())) {
return false;
}
if (claims.getExpiration() != null && new Date().after(claims.getExpiration())) {
return false;
}
return true;
}
public static void main(String[] args) throws Exception {
if(Files.notExists(Paths.get(RSA_PRIVATE_KEY_PATH))){
KeyPair keyPair = RsaKeyUtil.generateKeyPair(2048);
RsaKeyUtil.savePublicKey(keyPair.getPublic(), RSA_PUBLIC_KEY_PATH);
RsaKeyUtil.savePrivateKey(keyPair.getPrivate(), RSA_PRIVATE_KEY_PATH);
}
String token = createToken("test");
System.out.println("私钥加密:"+token);
PublicKey rsaPublicKey = getRsaPublicKey();
Claims claims = Jwts.parser().setSigningKey(rsaPublicKey).parseClaimsJws(token).getBody();
System.out.println("公钥解密:"+claims.getSubject());
}
}
RS256、RS384、RS512的区别
RS256:使用RSA和SHA-256算法进行签名。
RS384:使用RSA和SHA-384算法进行签名。
RS512:使用RSA和SHA-512算法进行签名。
ES签名算法
特点:与RS算法类似,ECDSA也是非对称加密算法,但使用的是椭圆曲线加密,通常比RSA算法更快,签名和验证过程更高效。同时,ECDSA的密钥长度较短,有助于减少Token的大小。
public class EsKeyUtil {
/**
* 生成RSA密钥对
*
* @param keySize 密钥长度(例如2048)
* @return 生成的KeyPair对象
* @throws NoSuchAlgorithmException 如果指定的算法不存在
*/
public static KeyPair generateKeyPair(int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(keySize);
return keyGen.generateKeyPair();
}
/**
* 将私钥保存到文件
*
* @param privateKey 私钥
* @param filename 文件名
* @throws Exception 如果发生错误
*/
public static void savePrivateKey(PrivateKey privateKey, String filename) throws Exception {
Path path = Paths.get(filename);
if (Files.notExists(path.getParent())) {
Files.createDirectories(path.getParent());
}
if(Files.notExists(path)){
Files.createFile(path);
}
byte[] encodedKey = privateKey.getEncoded();
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(encodedKey);
String pemString = "-----BEGIN PRIVATE KEY-----\n" +
Base64.getEncoder().encodeToString(pkcs8KeySpec.getEncoded()) +
"\n-----END PRIVATE KEY-----";
Files.write(path, pemString.getBytes());
}
/**
* 将公钥保存到文件
*
* @param publicKey 公钥
* @param filename 文件名
* @throws Exception 如果发生错误
*/
public static void savePublicKey(PublicKey publicKey, String filename) throws Exception {
Path path = Paths.get(filename);
// 确保父目录存在
if (Files.notExists(path.getParent())) {
Files.createDirectories(path.getParent());
}
if(Files.notExists(path)){
Files.createFile(path);
}
byte[] encodedKey = publicKey.getEncoded();
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(encodedKey);
String pemString = "-----BEGIN PUBLIC KEY-----\n" +
Base64.getEncoder().encodeToString(x509KeySpec.getEncoded()) +
"\n-----END PUBLIC KEY-----";
Files.write(path, pemString.getBytes());
}
}
@Slf4j
public class JwtEsUtil {
private static final String ES_PRIVATE_KEY_PATH="security/jwt/private_es.key";
private static final String ES_PUBLIC_KEY_PATH="security/jwt/public_es.key";
private static final Long EXPIRATION_TIME = 1800000L;
public static String generateToken(String key) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return createToken(key);
}
private static PrivateKey getEsPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Path path = Paths.get(ES_PRIVATE_KEY_PATH);
if(Files.notExists(path)){
throw new FileNotFoundException("私钥文件不存在");
}
byte[] bytes = Files.readAllBytes(path);
String privateKeyString = new String(bytes, StandardCharsets.UTF_8);
String privateKeyPem = privateKeyString.substring(28, privateKeyString.length() - 26);
byte[] decoded = Base64.getDecoder().decode(privateKeyPem);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePrivate(keySpec);
}
private static PublicKey getEsPublicKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Path path = Paths.get(ES_PUBLIC_KEY_PATH);
if(Files.notExists(path)){
throw new FileNotFoundException("公钥文件不存在");
}
byte[] bytes = Files.readAllBytes(path);
String publicKeyString = new String(bytes, StandardCharsets.UTF_8);
String publicKeyPem = publicKeyString.substring(27, publicKeyString.length() - 25);
byte[] decoded = Base64.getDecoder().decode(publicKeyPem);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePublic(keySpec);
}
/**
* 私有方法,用于实际生成 JWT
*
* @param subject
* @return
*/
private static String createToken(String subject) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getEsPrivateKey();
Claims claims = Jwts.claims()
.setId(UUID.randomUUID().toString())
.setSubject(subject)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.setIssuer("gateway-auth")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setNotBefore(new Date(System.currentTimeMillis()));
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.ES256, privateKey)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串
}
private static String createToken(Claims claims) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getEsPrivateKey();
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.ES256,privateKey)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串.
}
/**
* 判断令牌是否是系统签发,且是否在使用时间范围内
* @param token 令牌
* @return 校验正常 返回 true,校验失败返回false
*/
public static boolean validateToken(String token) {
try {
PrivateKey privateKey = getEsPrivateKey();
Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token);
if (!isTokenTimeValidate(token)) {
return false;
}
} catch (Exception e) {
log.error("令牌校验失败,失败令牌信息为:{}", token);
return false;
}
return true;
}
/**
* 判断令牌是否到了刷新时间,如果到了刷新时间返回新令牌,否则返回旧令牌
* @param token
* @return
*/
public static String refreshToken(String token) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getEsPrivateKey();
Claims claims = Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token).getBody();
Date expiration = claims.getExpiration();
long difference = expiration.getTime()-System.currentTimeMillis();
if((double)difference/EXPIRATION_TIME<0.3d){
claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));
return createToken(claims);
}
return token;
}
public static <T> T getClaimParam(String token, String paraName, Class<T> type) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getEsPrivateKey();
Claims claims = Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token).getBody();
return claims.get(paraName, type);
}
private static Boolean isTokenTimeValidate(String token) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getEsPrivateKey();
Claims claims = Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token).getBody();
if (claims.getNotBefore() != null && !claims.getNotBefore().before(new Date())) {
return false;
}
if (claims.getExpiration() != null && new Date().after(claims.getExpiration())) {
return false;
}
return true;
}
public static void main(String[] args) throws Exception {
if(Files.notExists(Paths.get(ES_PRIVATE_KEY_PATH))){
KeyPair keyPair = RsaKeyUtil.generateKeyPair(2048);
RsaKeyUtil.savePublicKey(keyPair.getPublic(), ES_PUBLIC_KEY_PATH);
RsaKeyUtil.savePrivateKey(keyPair.getPrivate(), ES_PRIVATE_KEY_PATH);
}
String token = createToken("test");
System.out.println("私钥加密:"+token);
PublicKey rsaPublicKey = getEsPublicKey();
Claims claims = Jwts.parser().setSigningKey(rsaPublicKey).parseClaimsJws(token).getBody();
System.out.println("公钥解密:"+claims.getSubject());
}
}
ES256、ES384、ES512的区别
ES256:使用椭圆曲线加密算法ECDSA和SHA-256进行签名。
ES384:使用ECDSA和SHA-384进行签名。
ES512:使用ECDSA和SHA-512进行签名
PS签名算法
特点:PS算法是RSA变种,使用填充方案来增加安全性,适用于需要更高安全性的场景
public class RsaKeyUtil {
/**
* 生成RSA密钥对
*
* @param keySize 密钥长度(例如2048)
* @return 生成的KeyPair对象
* @throws NoSuchAlgorithmException 如果指定的算法不存在
*/
public static KeyPair generateKeyPair(int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(keySize);
return keyGen.generateKeyPair();
}
/**
* 将私钥保存到文件
*
* @param privateKey 私钥
* @param filename 文件路径
* @throws Exception 如果发生错误
*/
public static void savePrivateKey(PrivateKey privateKey, String filename) throws Exception {
Path path = Paths.get(filename);
if (Files.notExists(path.getParent())) {
Files.createDirectories(path.getParent());
}
if(Files.notExists(path)){
Files.createFile(path);
}
byte[] encodedKey = privateKey.getEncoded();
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(encodedKey);
String pemString = "-----BEGIN PRIVATE KEY-----\n" +
Base64.getEncoder().encodeToString(pkcs8KeySpec.getEncoded()) +
"\n-----END PRIVATE KEY-----";
Files.write(path, pemString.getBytes());
}
/**
* 将公钥保存到文件
*
* @param publicKey 公钥
* @param filename 文件路径
* @throws Exception 如果发生错误
*/
public static void savePublicKey(PublicKey publicKey, String filename) throws Exception {
Path path = Paths.get(filename);
// 确保父目录存在
if (Files.notExists(path.getParent())) {
Files.createDirectories(path.getParent());
}
if(Files.notExists(path)){
Files.createFile(path);
}
byte[] encodedKey = publicKey.getEncoded();
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(encodedKey);
String pemString = "-----BEGIN PUBLIC KEY-----\n" +
Base64.getEncoder().encodeToString(x509KeySpec.getEncoded()) +
"\n-----END PUBLIC KEY-----";
Files.write(path, pemString.getBytes());
}
}
@Slf4j
public class JwtPsUtil {
private static final String RSA_PRIVATE_KEY_PATH="security/jwt/private.key";
private static final String RSA_PUBLIC_KEY_PATH="security/jwt/public.key";
private static final Long EXPIRATION_TIME = 1800000L;
public static String generateToken(String key) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return createToken(key);
}
private static PrivateKey getRsaPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Path path = Paths.get(RSA_PRIVATE_KEY_PATH);
if(Files.notExists(path)){
throw new FileNotFoundException("私钥文件不存在");
}
byte[] bytes = Files.readAllBytes(path);
String privateKeyString = new String(bytes, StandardCharsets.UTF_8);
String privateKeyPem = privateKeyString.substring(28, privateKeyString.length() - 26);
byte[] decoded = Base64.getDecoder().decode(privateKeyPem);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
private static PublicKey getRsaPublicKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Path path = Paths.get(RSA_PUBLIC_KEY_PATH);
if(Files.notExists(path)){
throw new FileNotFoundException("公钥文件不存在");
}
byte[] bytes = Files.readAllBytes(path);
String publicKeyString = new String(bytes, StandardCharsets.UTF_8);
String publicKeyPem = publicKeyString.substring(27, publicKeyString.length() - 25);
byte[] decoded = Base64.getDecoder().decode(publicKeyPem);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
/**
* 私有方法,用于实际生成 JWT
*
* @param subject
* @return
*/
private static String createToken(String subject) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
Claims claims = Jwts.claims()
.setId(UUID.randomUUID().toString())
.setSubject(subject)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.setIssuer("gateway-auth")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setNotBefore(new Date(System.currentTimeMillis()));
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.PS256, privateKey)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串
}
private static String createToken(Claims claims) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
return Jwts.builder()//开始构建 JWT 的构建器
.setClaims(claims)//设置 JWT 中的声明
.signWith(SignatureAlgorithm.PS256,privateKey)//使用指定的算法(HS256)和密钥对 JWT 进行签名
.compact();//生成最终的 JWT 字符串.
}
/**
* 判断令牌是否是系统签发,且是否在使用时间范围内
* @param token 令牌
* @return 校验正常 返回 true,校验失败返回false
*/
public static boolean validateToken(String token) {
try {
PrivateKey privateKey = getRsaPrivateKey();
Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token);
if (!isTokenTimeValidate(token)) {
return false;
}
} catch (Exception e) {
log.error("令牌校验失败,失败令牌信息为:{}", token);
return false;
}
return true;
}
/**
* 判断令牌是否到了刷新时间,如果到了刷新时间返回新令牌,否则返回旧令牌
* @param token
* @return
*/
public static String refreshToken(String token) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
Claims claims = Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token).getBody();
Date expiration = claims.getExpiration();
long difference = expiration.getTime()-System.currentTimeMillis();
if((double)difference/EXPIRATION_TIME<0.3d){
claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));
return createToken(claims);
}
return token;
}
public static <T> T getClaimParam(String token, String paraName, Class<T> type) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
Claims claims = Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token).getBody();
return claims.get(paraName, type);
}
private static Boolean isTokenTimeValidate(String token) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = getRsaPrivateKey();
Claims claims = Jwts.parser().setSigningKey(privateKey).parseClaimsJws(token).getBody();
if (claims.getNotBefore() != null && !claims.getNotBefore().before(new Date())) {
return false;
}
if (claims.getExpiration() != null && new Date().after(claims.getExpiration())) {
return false;
}
return true;
}
public static void main(String[] args) throws Exception {
if(Files.notExists(Paths.get(RSA_PRIVATE_KEY_PATH))){
KeyPair keyPair = RsaKeyUtil.generateKeyPair(2048);
RsaKeyUtil.savePublicKey(keyPair.getPublic(), RSA_PUBLIC_KEY_PATH);
RsaKeyUtil.savePrivateKey(keyPair.getPrivate(), RSA_PRIVATE_KEY_PATH);
}
String token = createToken("test");
System.out.println("私钥加密:"+token);
PublicKey rsaPublicKey = getRsaPublicKey();
Claims claims = Jwts.parser().setSigningKey(rsaPublicKey).parseClaimsJws(token).getBody();
System.out.println("公钥解密:"+claims.getSubject());
}
}
PS256、PS384、PS512的区别
PS256:使用RSASSA-PSS和SHA-256进行签名。
PS384:使用RSASSA-PSS和SHA-384进行签名。
PS512:使用RSASSA-PSS和SHA-512进行签名。