package com.ycl.utils; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.ycl.entity.auth.AuthInfo; import com.ycl.enums.common.ResultCode; import com.ycl.exception.ApiException; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * JwtToken生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * Created by macro on 2018/4/26. */ public class JwtTokenUtil { private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class); private static final String CLAIM_KEY_INFO = "sub"; private static final String CLAIM_KEY_CREATED = "created"; @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private Long expiration; @Value("${jwt.tokenHead}") private String tokenHead; /** * 根据负责生成JWT的token */ private String generateToken(Map claims) { return Jwts.builder() .setClaims(claims) .setExpiration(generateExpirationDate()) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } /** * 从token中获取JWT中的负载 */ private Claims getClaimsFromToken(String token) { Claims claims = null; try { claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } catch (Exception e) { LOGGER.info("JWT格式验证失败:{}", token); } return claims; } /** * 生成token的过期时间 */ private Date generateExpirationDate() { return new Date(System.currentTimeMillis() + expiration * 1000); } /** * 从token中获取登录用户名 */ public String getUserNameFromToken(String token) { String username; try { Claims claims = getClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { username = null; } return username; } /** * 验证token是否还有效 * * @param token 客户端传入的token * @param userDetails 从数据库中查询出来的用户信息 */ public boolean validateToken(String token, UserDetails userDetails) { String username = parseToken(token).getUsername(); return username.equals(userDetails.getUsername()) && !isTokenExpired(token); } /** * 判断token是否已经失效 */ private boolean isTokenExpired(String token) { Date expiredDate = getExpiredDateFromToken(token); return expiredDate.before(new Date()); } /** * 从token中获取过期时间 */ private Date getExpiredDateFromToken(String token) { Claims claims = getClaimsFromToken(token); return claims.getExpiration(); } /** * 根据用户信息生成token */ public String generateToken(UserDetails userDetails) { Map claims = new HashMap<>(); claims.put(CLAIM_KEY_INFO, userDetails.getUsername()); claims.put(CLAIM_KEY_CREATED, new Date()); return generateToken(claims); } /** * 当原来的token没过期时是可以刷新的 * * @param oldToken 带tokenHead的token */ public String refreshHeadToken(String oldToken) { if (StrUtil.isEmpty(oldToken)) { return null; } String token = oldToken.substring(tokenHead.length()); if (StrUtil.isEmpty(token)) { return null; } //token校验不通过 Claims claims = getClaimsFromToken(token); if (claims == null) { return null; } //如果token已经过期,不支持刷新 if (isTokenExpired(token)) { return null; } //如果token在30分钟之内刚刷新过,返回原token if (tokenRefreshJustBefore(token, 30 * 60)) { return token; } else { claims.put(CLAIM_KEY_CREATED, new Date()); return generateToken(claims); } } /** * 判断token在指定时间内是否刚刚刷新过 * * @param token 原token * @param time 指定时间(秒) */ private boolean tokenRefreshJustBefore(String token, int time) { Claims claims = getClaimsFromToken(token); Date created = claims.get(CLAIM_KEY_CREATED, Date.class); Date refreshDate = new Date(); //刷新时间在创建时间的指定时间内 if (refreshDate.after(created) && refreshDate.before(DateUtil.offsetSecond(created, time))) { return true; } return false; } /** * 根据用户id,用户名生成token */ public String generateToken(long userId, String username) { Map claims = new HashMap<>(); AuthInfo authInfo = new AuthInfo(); authInfo.setUserId(userId); authInfo.setUsername(username); claims.put(CLAIM_KEY_INFO, JSONObject.toJSONString(authInfo)); claims.put(CLAIM_KEY_CREATED, new Date()); return generateToken(claims); } /** * 根据token解析出AuthInfo * @param token * @return */ public AuthInfo parseToken(String token) { AuthInfo authInfo = null; try { if (isTokenExpired(token)) { throw new ApiException(ResultCode.NOT_LOGGED); } Claims claims = getClaimsFromToken(token); String subject = claims.getSubject(); authInfo = JSON.parseObject(subject, AuthInfo.class); } catch (Exception e) { throw new ApiException(e.getMessage()); } return authInfo; } }