refactor(auth): 优化令牌管理和安全性验证逻辑

This commit is contained in:
Ray.Hao
2025-12-03 20:59:38 +08:00
parent 5fa2e08aad
commit 96676f487e
4 changed files with 49 additions and 57 deletions

View File

@@ -152,10 +152,8 @@ public class AuthServiceImpl implements AuthService {
*/ */
@Override @Override
public void logout() { public void logout() {
String token = SecurityUtils.getTokenFromRequest(); String token = SecurityUtils.getAccessToken();
if (StrUtil.isNotBlank(token) && token.startsWith(SecurityConstants.BEARER_TOKEN_PREFIX )) { if (StrUtil.isNotBlank(token)) {
token = token.substring(SecurityConstants.BEARER_TOKEN_PREFIX .length());
// 将JWT令牌加入黑名单
tokenManager.invalidateToken(token); tokenManager.invalidateToken(token);
// 清除Security上下文 // 清除Security上下文
SecurityContextHolder.clearContext(); SecurityContextHolder.clearContext();

View File

@@ -132,7 +132,6 @@ public class JwtTokenManager implements TokenManager {
* @return 是否有效 * @return 是否有效
*/ */
private boolean validateToken(String token, boolean validateRefreshToken) { private boolean validateToken(String token, boolean validateRefreshToken) {
try {
JWT jwt = JWTUtil.parseToken(token); JWT jwt = JWTUtil.parseToken(token);
// 检查 Token 是否有效(验签 + 是否过期) // 检查 Token 是否有效(验签 + 是否过期)
boolean isValid = jwt.setKey(secretKey).validate(0); boolean isValid = jwt.setKey(secretKey).validate(0);
@@ -149,6 +148,7 @@ public class JwtTokenManager implements TokenManager {
} }
} }
// 2. 校验安全版本号(用于按用户维度失效历史 Token // 2. 校验安全版本号(用于按用户维度失效历史 Token
// 场景示例:用户修改密码、被管理员强制下线、手动“踢所有端”后,将用户安全版本号 +1旧版本 Token 全部失效
Long userId = payloads.getLong(JwtClaimConstants.USER_ID); Long userId = payloads.getLong(JwtClaimConstants.USER_ID);
if (userId != null) { if (userId != null) {
// 老版本 Token 可能没有 SECURITY_VERSION 声明,视为 0 版本 // 老版本 Token 可能没有 SECURITY_VERSION 声明,视为 0 版本
@@ -166,15 +166,12 @@ public class JwtTokenManager implements TokenManager {
} }
// 3. 判断是否在黑名单中,如果在,则返回 false 标识Token无效 // 3. 判断是否在黑名单中,如果在,则返回 false 标识Token无效
// 场景示例:单点退出登录、后台手动注销某个会话、封禁账号后立即阻断当前 Token 等
if (Boolean.TRUE.equals(redisTemplate.hasKey(StrUtil.format(RedisConstants.Auth.BLACKLIST_TOKEN, jti)))) { if (Boolean.TRUE.equals(redisTemplate.hasKey(StrUtil.format(RedisConstants.Auth.BLACKLIST_TOKEN, jti)))) {
return false; return false;
} }
} }
return isValid; return isValid;
} catch (Exception gitignore) {
// token 验证
}
return false;
} }
/** /**
@@ -210,7 +207,6 @@ public class JwtTokenManager implements TokenManager {
// 永不过期的Token永久加入黑名单 // 永不过期的Token永久加入黑名单
redisTemplate.opsForValue().set(blacklistTokenKey, Boolean.TRUE); redisTemplate.opsForValue().set(blacklistTokenKey, Boolean.TRUE);
} }
;
} }
/** /**

View File

@@ -165,8 +165,8 @@ public class RedisTokenManager implements TokenManager {
*/ */
@Override @Override
public void invalidateToken(String token) { public void invalidateToken(String token) {
OnlineUser onlineUser = (OnlineUser) redisTemplate.opsForValue().get(formatTokenKey(token)); Object value = redisTemplate.opsForValue().get(formatTokenKey(token));
if (onlineUser != null) { if (value instanceof OnlineUser onlineUser) {
Long userId = onlineUser.getUserId(); Long userId = onlineUser.getUserId();
invalidateUserSessions(userId); invalidateUserSessions(userId);
} }
@@ -186,20 +186,18 @@ public class RedisTokenManager implements TokenManager {
// 1. 删除访问令牌相关 // 1. 删除访问令牌相关
String userAccessKey = StrUtil.format(RedisConstants.Auth.USER_ACCESS_TOKEN, userId); String userAccessKey = StrUtil.format(RedisConstants.Auth.USER_ACCESS_TOKEN, userId);
Object accessTokenValue = redisTemplate.opsForValue().get(userAccessKey); Object accessTokenValue = redisTemplate.opsForValue().get(userAccessKey);
Optional.of(accessTokenValue) if (accessTokenValue instanceof String accessToken) {
.map(String.class::cast) redisTemplate.delete(formatTokenKey(accessToken));
.ifPresent(accessToken -> redisTemplate.delete(formatTokenKey(accessToken))); }
// 无论是否存在访问令牌映射,都尝试删除 userAccessKey // 无论是否存在访问令牌映射,都尝试删除 userAccessKey
redisTemplate.delete(userAccessKey); redisTemplate.delete(userAccessKey);
// 2. 删除刷新令牌相关 // 2. 删除刷新令牌相关
String userRefreshKey = StrUtil.format(RedisConstants.Auth.USER_REFRESH_TOKEN, userId); String userRefreshKey = StrUtil.format(RedisConstants.Auth.USER_REFRESH_TOKEN, userId);
Object refreshTokenValue = redisTemplate.opsForValue().get(userRefreshKey); Object refreshTokenValue = redisTemplate.opsForValue().get(userRefreshKey);
Optional.of(refreshTokenValue) if (refreshTokenValue instanceof String refreshToken) {
.map(String.class::cast) redisTemplate.delete(StrUtil.format(RedisConstants.Auth.REFRESH_TOKEN_USER, refreshToken));
.ifPresent(refreshToken -> }
redisTemplate.delete(StrUtil.format(RedisConstants.Auth.REFRESH_TOKEN_USER, refreshToken))
);
// 同样清理 userRefreshKey 本身 // 同样清理 userRefreshKey 本身
redisTemplate.delete(userRefreshKey); redisTemplate.delete(userRefreshKey);
} }
@@ -237,9 +235,9 @@ public class RedisTokenManager implements TokenManager {
// 单设备登录控制,删除旧的访问令牌 // 单设备登录控制,删除旧的访问令牌
if (!allowMultiLogin) { if (!allowMultiLogin) {
Object oldAccessTokenValue = redisTemplate.opsForValue().get(userAccessKey); Object oldAccessTokenValue = redisTemplate.opsForValue().get(userAccessKey);
Optional.of(oldAccessTokenValue) if (oldAccessTokenValue instanceof String oldAccessToken) {
.map(String.class::cast) redisTemplate.delete(formatTokenKey(oldAccessToken));
.ifPresent(oldAccessToken -> redisTemplate.delete(formatTokenKey(oldAccessToken))); }
} }
// 存储访问令牌映射用户ID -> 访问令牌),用于单设备登录控制删除旧的访问令牌和刷新令牌时删除旧令牌 // 存储访问令牌映射用户ID -> 访问令牌),用于单设备登录控制删除旧的访问令牌和刷新令牌时删除旧令牌
setRedisValue(userAccessKey, accessToken, securityProperties.getSession().getAccessTokenTimeToLive()); setRedisValue(userAccessKey, accessToken, securityProperties.getSession().getAccessTokenTimeToLive());

View File

@@ -113,7 +113,7 @@ public class SecurityUtils {
* *
* @return Token 字符串 * @return Token 字符串
*/ */
public static String getTokenFromRequest() { public static String getAccessToken() {
ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()); ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
if(Objects.isNull(servletRequestAttributes)) { if(Objects.isNull(servletRequestAttributes)) {
return null; return null;