diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java index 55e36174..b3f0042c 100644 --- a/eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java +++ b/eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java @@ -58,6 +58,24 @@ public class RedisUtils { 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 获取过期时间 * @param key 键 不能为null diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java index 1949fe72..501b7a19 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java @@ -18,7 +18,6 @@ package me.zhengjie.modules.security.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; /** * Jwt参数配置 @@ -48,6 +47,12 @@ public class SecurityProperties { /** 验证码 key */ private String codeKey; + /** token 续期检查 */ + private Long detect; + + /** 续期时间 */ + private Long renew; + public String getTokenStartWith() { return tokenStartWith + " "; } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java index cc07f534..4acf8dc9 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java @@ -50,16 +50,18 @@ public class TokenFilter extends GenericFilterBean { String requestRri = httpServletRequest.getRequestURI(); // 验证 token 是否存在 OnlineUserDto onlineUserDto = null; + SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class); try { - SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class); OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class); onlineUserDto = onlineUserService.getOne(properties.getOnlineKey() + token); } catch (ExpiredJwtException e) { log.error(e.getMessage()); } - if (onlineUserDto != null && StringUtils.hasText(token) && tokenProvider.validateToken(token)) { + if (onlineUserDto != null && StringUtils.hasText(token)) { Authentication authentication = tokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); + // Token 续期 + tokenProvider.checkRenewal(token); log.debug("set Authentication to security context for '{}', uri: {}", authentication.getName(), requestRri); } else { log.debug("no valid JWT token found, uri: {}", requestRri); diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java index 37885494..a51e2538 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java @@ -15,6 +15,8 @@ */ 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 io.jsonwebtoken.*; import io.jsonwebtoken.io.Decoders; @@ -22,6 +24,7 @@ import io.jsonwebtoken.security.Keys; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.modules.security.config.SecurityProperties; +import me.zhengjie.utils.RedisUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -35,6 +38,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** @@ -46,6 +50,7 @@ import java.util.stream.Collectors; public class TokenProvider implements InitializingBean { private final SecurityProperties properties; + private final RedisUtils redisUtils; private static final String AUTHORITIES_KEY = "auth"; private Key key; @@ -60,14 +65,10 @@ public class TokenProvider implements InitializingBean { .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(",")); - long now = (new Date()).getTime(); - Date validity = new Date(now + properties.getTokenValidityInSeconds()); - return Jwts.builder() .setSubject(authentication.getName()) .claim(AUTHORITIES_KEY, authorities) .signWith(key, SignatureAlgorithm.HS512) - .setExpiration(validity) .compact(); } @@ -78,7 +79,7 @@ public class TokenProvider implements InitializingBean { .getBody(); // fix bug: 当前用户如果没有任何权限时,在输入用户名后,刷新验证码会抛IllegalArgumentException - Object authoritiesStr = claims.get(AUTHORITIES_KEY); + Object authoritiesStr = claims.get(AUTHORITIES_KEY); Collection authorities = ObjectUtil.isNotEmpty(authoritiesStr) ? Arrays.stream(authoritiesStr.toString().split(",")) @@ -90,24 +91,20 @@ public class TokenProvider implements InitializingBean { return new UsernamePasswordAuthenticationToken(principal, token, authorities); } - boolean validateToken(String authToken) { - try { - Jwts.parser().setSigningKey(key).parseClaimsJws(authToken); - return true; - } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { - log.info("Invalid JWT signature."); - e.printStackTrace(); - } catch (ExpiredJwtException e) { - log.info("Expired JWT token."); - e.printStackTrace(); - } catch (UnsupportedJwtException e) { - log.info("Unsupported JWT token."); - e.printStackTrace(); - } catch (IllegalArgumentException e) { - log.info("JWT token compact of handler are invalid."); - e.printStackTrace(); + /** + * @param token 需要检查的token + */ + public void checkRenewal(String token){ + // 判断是否续期token,计算token的过期时间 + long time = redisUtils.getExpire(properties.getOnlineKey() + token) * 1000; + Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time); + // 判断当前时间与过期时间的时间差 + long differ = expireDate.getTime() - new Date().getTime(); + // 如果在续期检查的范围内,则续期 + if(differ <= properties.getDetect()){ + long renew = time + properties.getRenew(); + redisUtils.expire(properties.getOnlineKey() + token, renew, TimeUnit.MILLISECONDS); } - return false; } public String getToken(HttpServletRequest request){ diff --git a/eladmin-system/src/main/resources/config/application-dev.yml b/eladmin-system/src/main/resources/config/application-dev.yml index 40c63840..8ed36a80 100644 --- a/eladmin-system/src/main/resources/config/application-dev.yml +++ b/eladmin-system/src/main/resources/config/application-dev.yml @@ -48,6 +48,10 @@ jwt: online-key: online-token # 验证码 code-key: code-key + # token 续期检查时间范围(默认30分钟,单位毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期 + detect: 1800000 + # 续期时间范围,默认1小时,单位毫秒 + renew: 3600000 #是否允许生成代码,生产环境设置为false generator: diff --git a/eladmin-system/src/main/resources/config/application-prod.yml b/eladmin-system/src/main/resources/config/application-prod.yml index 3df2fd4c..7304f2d8 100644 --- a/eladmin-system/src/main/resources/config/application-prod.yml +++ b/eladmin-system/src/main/resources/config/application-prod.yml @@ -50,6 +50,10 @@ jwt: online-key: online-token # 验证码 code-key: code-key + # token 续期检查时间范围(默认30分钟,单位默认毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期 + detect: 1800000 + # 续期时间范围,默认 1小时,这里单位毫秒 + renew: 3600000 #是否允许生成代码,生产环境设置为false generator: