package com.boot.utils;import com.boot.constant.TokenConstant;import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import lombok.extern.slf4j.Slf4j;import java.util.Date;import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap;/** * JWT工具类 * 内置access_token和refresh_token可实现JWT token的无状态续签 * @author youzhengjie 2022-09-21 20:56:56 */@Slf4jpublic class JwtUtil {private static final String ACCESS_SECRET="security-jwt-accesstoken";//access_token密钥private static final String REFRESH_SECRET="security-jwt-refreshtoken";//refresh_token密钥//access_token过期时间 60*1000*60L=60分钟过期private static final Long ACCESS_TOKEN_EXPIRATION=60 *1000 * 60L;//refresh_token过期时间 60*1000*180L=180分钟过期private static final Long REFRESH_TOKEN_EXPIRATION=60 *1000 * 180L;/** * * @return 随机的uuid */public static String getUUID(){String token = UUID.randomUUID().toString().replaceAll("-", "");return token;}/** * @return 同时返回access_token和refresh_token的map集合 * 通过map集合.get(TokenConstant.ACCESSTOKEN)可以拿到access token * 通过map集合.get(TokenConstant.REFRESHTOKEN)可以拿到refresh token */public static Map<String,String> createAccessTokenAndRefreshToken(String subject){Map<String,String> tokenMap=new ConcurrentHashMap<>();String accessToken = createAccessToken(subject);String refreshToken = createRefreshToken(subject);tokenMap.put(TokenConstant.ACCESSTOKEN,accessToken);tokenMap.put(TokenConstant.REFRESHTOKEN,refreshToken);return tokenMap;}/** *根据用户id生成access_token * @param subject 例如userid * @return 返回access token */public static String createAccessToken(String subject){long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);long expMillis = nowMillis + ACCESS_TOKEN_EXPIRATION; //access_token过期时间return Jwts.builder().setId(getUUID())//唯一的ID.setSubject(subject) //数据内容就存放到这里.setIssuer("youzhengjie") // 签发者.setIssuedAt(now)// 颁发token时间.setExpiration(new Date(expMillis))//token过期时间.signWith(SignatureAlgorithm.HS256,ACCESS_SECRET).compact();}/** * 下面的代码已经自带了校验签名和过期时间,如果签名不正确或者过期了则直接会抛出异常 * @param accessToken 解析access_token * @return */public static Claims parseAccessToken(String accessToken) {Claims claims = null;try {claims = Jwts.parser().setSigningKey(ACCESS_SECRET) .parseClaimsJws(accessToken) .getBody();} catch (Exception ex) {ex.printStackTrace();}return claims;}/** *根据用户id生成refresh_token * @param subject 例如userid * @return 返回refresh token */public static String createRefreshToken(String subject){long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);long expMillis = nowMillis + REFRESH_TOKEN_EXPIRATION; //refresh_token过期时间return Jwts.builder().setId(getUUID())//唯一的ID.setSubject(subject) //数据内容就存放到这里.setIssuer("youzhengjie") // 签发者.setIssuedAt(now)// 颁发token时间.setExpiration(new Date(expMillis))//token过期时间.signWith(SignatureAlgorithm.HS256,REFRESH_SECRET).compact();}/** * 下面的代码已经自带了校验签名和过期时间,如果签名不正确或者过期了则直接会抛出异常 * @param refreshToken 解析refresh_token * @return 如果refreshToken过期了则返回null */public static Claims parseRefreshToken(String refreshToken) {Claims claims = null;try {claims = Jwts.parser().setSigningKey(REFRESH_SECRET).parseClaimsJws(refreshToken).getBody();} catch (Exception ex) {ex.printStackTrace();}return claims;}/** *判断token是否可以被刷新(主要就是看refresh_token是否过期,如果没有过期则可以刷新token) * @param refreshToken * @return true就是refreshtoken可以使用,反之则不行 */public static boolean canRefresh(String refreshToken){Claims claims = parseRefreshToken(refreshToken);return !isRefreshTokenExpired(claims);} /** * 去刷新token,本质上就是重新生成新的accesstoken和refreshtoken并替代掉前端localstorage的旧的accesstoken和refreshtoken * @param refreshToken * @return 如果不能刷新token,则返回null */public static Map<String, String> toRefreshToken(String refreshToken){//先判断refreshToken是否过期,看看能不能刷新token,否则返回nullif(!canRefresh(refreshToken)){return null;}Claims claims = parseRefreshToken(refreshToken);String subject = claims.getSubject();return createAccessTokenAndRefreshToken(subject);//重新生成accesstoken和refreshtoken} /** *判断access_token是否过期 * @param claims * @return true就是过期了 */public static boolean isAccessTokenExpired(Claims claims) {Date expiredDate = claims.getExpiration();return expiredDate.before(new Date()); //expiredDate < new Date()true}/** *判断refresh_token是否过期 * @param claims * @return true就是过期了 */public static boolean isRefreshTokenExpired(Claims claims) {Date expiredDate = claims.getExpiration();return expiredDate.before(new Date()); //expiredDate < new Date()true}/** * 测试util * @param args */public static void main(String[] args) {String userid="123123999777";//1: 生成tokenMap<String, String> tokenMap = createAccessTokenAndRefreshToken(userid);String accesstoken = tokenMap.get(TokenConstant.ACCESSTOKEN);String refreshtoken = tokenMap.get(TokenConstant.REFRESHTOKEN);log.warn(accesstoken);log.warn(refreshtoken);//2:解析tokenClaims c1 = parseAccessToken(accesstoken);Claims c2 = parseRefreshToken(refreshtoken);String s1 = c1.getSubject();System.out.println(s1);String s2 = c2.getSubject();System.out.println(s2);//3:刷新tokenMap<String, String> refreshTokenMap = toRefreshToken(refreshtoken);String accesstoken2 = refreshTokenMap.get(TokenConstant.ACCESSTOKEN);String refreshtoken2 = refreshTokenMap.get(TokenConstant.REFRESHTOKEN);log.warn("------------------");log.warn(accesstoken2);log.warn(refreshtoken2);Claims claims1 = parseAccessToken(accesstoken2);Claims claims2 = parseRefreshToken(refreshtoken2);System.out.println(claims1.getSubject());System.out.println(claims2.getSubject());}}