mirror of https://github.com/elunez/eladmin
[代码完善](v2.5): v2.5 beta Token 自动续期
Security Token 代码优化 与 Token 自动续期 可在 dev.yml 和 prod.yml 中管理 详情:https://www.ydyno.com/archives/1225.htmlpull/361/head^2
parent
b260985578
commit
adde56babe
|
@ -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
|
||||||
|
|
|
@ -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 + " ";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
|
||||||
try {
|
try {
|
||||||
SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
|
|
||||||
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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ public class TokenProvider implements InitializingBean {
|
||||||
.getBody();
|
.getBody();
|
||||||
|
|
||||||
// fix bug: 当前用户如果没有任何权限时,在输入用户名后,刷新验证码会抛IllegalArgumentException
|
// fix bug: 当前用户如果没有任何权限时,在输入用户名后,刷新验证码会抛IllegalArgumentException
|
||||||
Object authoritiesStr = claims.get(AUTHORITIES_KEY);
|
Object authoritiesStr = claims.get(AUTHORITIES_KEY);
|
||||||
Collection<? extends GrantedAuthority> authorities =
|
Collection<? extends GrantedAuthority> authorities =
|
||||||
ObjectUtil.isNotEmpty(authoritiesStr) ?
|
ObjectUtil.isNotEmpty(authoritiesStr) ?
|
||||||
Arrays.stream(authoritiesStr.toString().split(","))
|
Arrays.stream(authoritiesStr.toString().split(","))
|
||||||
|
@ -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){
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue