[代码完善](v2.5): v2.5 beta Token 自动续期

Security Token 代码优化 与 Token 自动续期
可在 dev.yml 和 prod.yml 中管理

详情:https://www.ydyno.com/archives/1225.html
pull/361/head^2
ZhengJie 2020-05-06 21:30:53 +08:00
parent b260985578
commit adde56babe
6 changed files with 55 additions and 25 deletions

View File

@ -58,6 +58,24 @@ public class RedisUtils {
return true; return true;
} }
/**
*
* @param key
* @param time ()
* @param timeUnit
*/
public boolean expire(String key, long time, TimeUnit timeUnit) {
try {
if (time > 0) {
redisTemplate.expire(key, time, timeUnit);
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/** /**
* key * key
* @param key null * @param key null

View File

@ -18,7 +18,6 @@ package me.zhengjie.modules.security.config;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/** /**
* Jwt * Jwt
@ -48,6 +47,12 @@ public class SecurityProperties {
/** 验证码 key */ /** 验证码 key */
private String codeKey; private String codeKey;
/** token 续期检查 */
private Long detect;
/** 续期时间 */
private Long renew;
public String getTokenStartWith() { public String getTokenStartWith() {
return tokenStartWith + " "; return tokenStartWith + " ";
} }

View File

@ -50,16 +50,18 @@ public class TokenFilter extends GenericFilterBean {
String requestRri = httpServletRequest.getRequestURI(); String requestRri = httpServletRequest.getRequestURI();
// 验证 token 是否存在 // 验证 token 是否存在
OnlineUserDto onlineUserDto = null; OnlineUserDto onlineUserDto = null;
try {
SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class); SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
try {
OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class); OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);
onlineUserDto = onlineUserService.getOne(properties.getOnlineKey() + token); onlineUserDto = onlineUserService.getOne(properties.getOnlineKey() + token);
} catch (ExpiredJwtException e) { } catch (ExpiredJwtException e) {
log.error(e.getMessage()); log.error(e.getMessage());
} }
if (onlineUserDto != null && StringUtils.hasText(token) && tokenProvider.validateToken(token)) { if (onlineUserDto != null && StringUtils.hasText(token)) {
Authentication authentication = tokenProvider.getAuthentication(token); Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
// Token 续期
tokenProvider.checkRenewal(token);
log.debug("set Authentication to security context for '{}', uri: {}", authentication.getName(), requestRri); log.debug("set Authentication to security context for '{}', uri: {}", authentication.getName(), requestRri);
} else { } else {
log.debug("no valid JWT token found, uri: {}", requestRri); log.debug("no valid JWT token found, uri: {}", requestRri);

View File

@ -15,6 +15,8 @@
*/ */
package me.zhengjie.modules.security.security; package me.zhengjie.modules.security.security;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import io.jsonwebtoken.*; import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.io.Decoders;
@ -22,6 +24,7 @@ import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhengjie.modules.security.config.SecurityProperties; import me.zhengjie.modules.security.config.SecurityProperties;
import me.zhengjie.utils.RedisUtils;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -35,6 +38,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -46,6 +50,7 @@ import java.util.stream.Collectors;
public class TokenProvider implements InitializingBean { public class TokenProvider implements InitializingBean {
private final SecurityProperties properties; private final SecurityProperties properties;
private final RedisUtils redisUtils;
private static final String AUTHORITIES_KEY = "auth"; private static final String AUTHORITIES_KEY = "auth";
private Key key; private Key key;
@ -60,14 +65,10 @@ public class TokenProvider implements InitializingBean {
.map(GrantedAuthority::getAuthority) .map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
long now = (new Date()).getTime();
Date validity = new Date(now + properties.getTokenValidityInSeconds());
return Jwts.builder() return Jwts.builder()
.setSubject(authentication.getName()) .setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities) .claim(AUTHORITIES_KEY, authorities)
.signWith(key, SignatureAlgorithm.HS512) .signWith(key, SignatureAlgorithm.HS512)
.setExpiration(validity)
.compact(); .compact();
} }
@ -90,24 +91,20 @@ public class TokenProvider implements InitializingBean {
return new UsernamePasswordAuthenticationToken(principal, token, authorities); return new UsernamePasswordAuthenticationToken(principal, token, authorities);
} }
boolean validateToken(String authToken) { /**
try { * @param token token
Jwts.parser().setSigningKey(key).parseClaimsJws(authToken); */
return true; public void checkRenewal(String token){
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { // 判断是否续期token,计算token的过期时间
log.info("Invalid JWT signature."); long time = redisUtils.getExpire(properties.getOnlineKey() + token) * 1000;
e.printStackTrace(); Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time);
} catch (ExpiredJwtException e) { // 判断当前时间与过期时间的时间差
log.info("Expired JWT token."); long differ = expireDate.getTime() - new Date().getTime();
e.printStackTrace(); // 如果在续期检查的范围内,则续期
} catch (UnsupportedJwtException e) { if(differ <= properties.getDetect()){
log.info("Unsupported JWT token."); long renew = time + properties.getRenew();
e.printStackTrace(); redisUtils.expire(properties.getOnlineKey() + token, renew, TimeUnit.MILLISECONDS);
} catch (IllegalArgumentException e) {
log.info("JWT token compact of handler are invalid.");
e.printStackTrace();
} }
return false;
} }
public String getToken(HttpServletRequest request){ public String getToken(HttpServletRequest request){

View File

@ -48,6 +48,10 @@ jwt:
online-key: online-token online-key: online-token
# 验证码 # 验证码
code-key: code-key code-key: code-key
# token 续期检查时间范围默认30分钟单位毫秒在token即将过期的一段时间内用户操作了则给用户的token续期
detect: 1800000
# 续期时间范围默认1小时单位毫秒
renew: 3600000
#是否允许生成代码生产环境设置为false #是否允许生成代码生产环境设置为false
generator: generator:

View File

@ -50,6 +50,10 @@ jwt:
online-key: online-token online-key: online-token
# 验证码 # 验证码
code-key: code-key code-key: code-key
# token 续期检查时间范围默认30分钟单位默认毫秒在token即将过期的一段时间内用户操作了则给用户的token续期
detect: 1800000
# 续期时间范围,默认 1小时这里单位毫秒
renew: 3600000
#是否允许生成代码生产环境设置为false #是否允许生成代码生产环境设置为false
generator: generator: