package com.gkhy.exam.framework.web.service; import cn.hutool.crypto.digest.DigestUtil; import com.gkhy.exam.common.constant.CacheConstant; import com.gkhy.exam.common.domain.model.LoginUser; import com.gkhy.exam.common.exception.ApiException; import com.gkhy.exam.common.utils.RedisUtils; import com.gkhy.exam.common.utils.StringUtils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @Slf4j @Component public class TokenService { //令牌自定义标识 public static final String HEADER="Authorization"; //令牌密钥 public static final String SECRET="gkhy123456@"; public static final String CLAIM_KEY_USERNAME = "sub"; //令牌有效期 public static final long EXPIRATION=12 * 60 * 60; //JWT的超期限时间(60*60*12)s protected static final long MILLIS_SECOND = 1000L; protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; private static final Long MILLIS_MINUTE_TEN = 20 * MILLIS_MINUTE; @Autowired private RedisUtils redisUtils; public LoginUser getLoginUser(HttpServletRequest request){ String token=getToken(request); if(!StringUtils.isBlank(token)){ try { // 解析对应的权限以及用户信息 String tagUsername = getUserNameFromToken(token); if(StringUtils.isBlank(tagUsername)){ return null; } String userKey = getTokenKey(tagUsername.substring(0,tagUsername.lastIndexOf("_"))); LoginUser user = (LoginUser) redisUtils.get(userKey); return user; } catch (Exception e) { log.error("获取用户信息异常'{}'", e.getMessage()); } } return null; } /** * 删除用户身份信息 */ public void delLoginUser(String username) { if (StringUtils.isNotEmpty(username)) { String userKey = getTokenKey(username); redisUtils.del(userKey); } } /** * 创建令牌 * * @param username 用户信息 * @return 令牌 */ public String createToken(String username) { Map claims = new HashMap<>(); claims.put(CLAIM_KEY_USERNAME, username); return createToken(claims); } public String createToken(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) { log.error("JWT格式验证失败:{}", token); } return claims; } /** * 从token中获取登录用户名 */ public String getUserNameFromToken(String token) { String username; try { Claims claims = getClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { username = null; } return username; } /** * 从token中获取过期时间 */ private Date getExpiredDateFromToken(String token) { Claims claims = getClaimsFromToken(token); return claims.getExpiration(); } /** * 验证token是否还有效 * * @param token 客户端传入的token * @param userDetails 从数据库中查询出来的用户信息 */ public boolean validateToken(String token, UserDetails userDetails) { String tagUsername = getUserNameFromToken(token); String username=tagUsername.substring(0,tagUsername.lastIndexOf("_")); if(StringUtils.isBlank(username)||!username.equals(userDetails.getUsername())){ return false; } String tokenKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+md5Encode(token)); String userKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+username); String cacheToken= (String) redisUtils.get(tokenKey); if(StringUtils.isBlank(cacheToken)||isTokenExpired(cacheToken)){ return false; } if(isNeedUpdate(cacheToken)){ String newToken=createToken(tagUsername); // 设置超时时间 redisUtils.set(tokenKey,newToken,EXPIRATION,TimeUnit.SECONDS); redisUtils.expire(userKey,(EXPIRATION)+2); } return true; } /** * 判断token是否已经失效 */ private boolean isTokenExpired(String token) { Date expiredDate = getExpiredDateFromToken(token); return expiredDate.before(new Date()); } /** * 生成token的过期时间 */ private Date generateExpirationDate() { return new Date(System.currentTimeMillis() + EXPIRATION * 1000); } public boolean isNeedUpdate(String token){ Date expertsAt =null; try { Claims claims = getClaimsFromToken(token); expertsAt = claims.getExpiration(); }catch (Exception e){ throw new ApiException("token非法无效"); } //如果剩余过期时间少于过期时常的一半时 需要更新 return (expertsAt.getTime()-System.currentTimeMillis()) < MILLIS_MINUTE_TEN; } /** * 获取请求token * * @param request * @return token */ private String getToken(HttpServletRequest request) { return request.getHeader(HEADER); } /** * 获取redis key * @param username * @return */ private String getTokenKey(String username) { return redisUtils.generateKey(CacheConstant.LOGIN_TOKEN_KEY + username); } /** * md5加密 * @param token * @return */ public static String md5Encode(String token){ return DigestUtil.md5Hex(token); } /** * 清空旧的登录信息,保证同时只有一个用户再登录,后登录用户会挤掉前一个用户 * @param username */ public void cacheUserToken(String username,String newToken){ String userKey = redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN + username); String oldToken = (String) redisUtils.get(userKey); if (StringUtils.isNotBlank(oldToken)) { String oldTokenkey = redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN +md5Encode(oldToken)); redisUtils.del(oldTokenkey); redisUtils.del(userKey); } redisUtils.set(userKey, newToken,(TokenService.EXPIRATION)+2, TimeUnit.SECONDS); String tokenKey= redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+md5Encode(newToken)); // 设置超时时间 redisUtils.set(tokenKey,newToken,TokenService.EXPIRATION, TimeUnit.SECONDS); } }