From 560298ae7fdd218450fb90c086cf8ffc18ec70fd Mon Sep 17 00:00:00 2001 From: cwchen <1048842385@qq.com> Date: Mon, 29 Sep 2025 13:46:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E8=AE=BE=E5=A4=87=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/web/service/TokenService.java | 79 ++++++++++++++++--- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/bonus-framework/src/main/java/com/bonus/framework/web/service/TokenService.java b/bonus-framework/src/main/java/com/bonus/framework/web/service/TokenService.java index 9cd4f4c..e705256 100644 --- a/bonus-framework/src/main/java/com/bonus/framework/web/service/TokenService.java +++ b/bonus-framework/src/main/java/com/bonus/framework/web/service/TokenService.java @@ -27,7 +27,7 @@ import io.jsonwebtoken.SignatureAlgorithm; /** * token验证处理 - * + * * @author bonus */ @Component @@ -58,7 +58,7 @@ public class TokenService /** * 获取用户身份信息 - * + * * @return 用户信息 */ public LoginUser getLoginUser(HttpServletRequest request) @@ -75,16 +75,23 @@ public class TokenService String usernameFromJwt = (String) claims.get(Constants.JWT_USERNAME); String userKey = getTokenKey(uuid); LoginUser user = redisCache.getCacheObject(userKey); + if (StringUtils.isNull(user)) { - // login_tokens:{uuid} 已不存在:视为会话被挤下线或已失效 - // 为确保前端能提示“其他设备登录”,在能解析出用户名时一律标记 + // login_tokens:{uuid} 已不存在:需要区分是过期还是被挤下线 if (StringUtils.isNotEmpty(usernameFromJwt)) { - request.setAttribute("forceLogoutByOtherDevice", Boolean.TRUE); + String mappedUuid = redisCache.getCacheObject(getUserTokenKey(usernameFromJwt)); + if (StringUtils.isNotEmpty(mappedUuid) && !uuid.equals(mappedUuid)) + { + // 用户名映射存在且指向其他uuid,说明是被其他设备挤下线 + request.setAttribute("forceLogoutByOtherDevice", Boolean.TRUE); + } + // 否则就是正常的token过期,不需要设置特殊标记 } return null; } + // 单端在线校验:username -> uuid 映射需要与当前token匹配 String username = user.getUsername(); String mappedUuid = redisCache.getCacheObject(getUserTokenKey(username)); @@ -142,7 +149,7 @@ public class TokenService /** * 创建令牌 - * + * * @param loginUser 用户信息 * @return 令牌 */ @@ -164,7 +171,7 @@ public class TokenService /** * 验证令牌有效期,相差不足20分钟,自动刷新缓存 - * + * * @param loginUser 登录信息 * @return 令牌 */ @@ -180,7 +187,7 @@ public class TokenService /** * 刷新令牌有效期 - * + * * @param loginUser 登录信息 */ public void refreshToken(LoginUser loginUser) @@ -202,7 +209,7 @@ public class TokenService /** * 设置用户代理信息 - * + * * @param loginUser 登录信息 */ public void setUserAgent(LoginUser loginUser) @@ -297,6 +304,9 @@ public class TokenService return getToken(ServletUtils.getRequest()); } + /** + * 根据token获取登录用户信息 + */ public LoginUser getLoginUser(String token){ if (StringUtils.isNotEmpty(token)){ try @@ -307,19 +317,23 @@ public class TokenService String usernameFromJwt = (String) claims.get(Constants.JWT_USERNAME); String userKey = getTokenKey(uuid); LoginUser user = redisCache.getCacheObject(userKey); + if (StringUtils.isNull(user)) { - // 若login_tokens无此uuid,检查用户名映射,若映射存在且指向其他uuid,则视为被其他设备挤下线 + // 若login_tokens无此uuid,检查用户名映射 if (StringUtils.isNotEmpty(usernameFromJwt)) { String mappedUuid = redisCache.getCacheObject(getUserTokenKey(usernameFromJwt)); if (StringUtils.isNotEmpty(mappedUuid) && !uuid.equals(mappedUuid)) { + // 被其他设备挤下线 return null; } + // 否则是正常过期,返回null } return null; } + String username = user.getUsername(); String mappedUuid = redisCache.getCacheObject(getUserTokenKey(username)); if (StringUtils.isEmpty(mappedUuid) || !uuid.equals(mappedUuid)) @@ -351,4 +365,47 @@ public class TokenService } redisCache.setCacheObject(userTokenKey, uuid, expireTime, TimeUnit.MINUTES); } -} + + /** + * 检查token是否过期(新增方法) + * + * @param token token + * @return true-过期, false-未过期 + */ + public boolean isTokenExpired(String token) { + try { + Claims claims = parseToken(token); + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = redisCache.getCacheObject(userKey); + return StringUtils.isNull(user); + } catch (Exception e) { + log.error("检查token过期状态异常'{}'", e.getMessage()); + return true; + } + } + + /** + * 检查是否被其他设备挤下线(新增方法) + * + * @param token token + * @return true-被挤下线, false-未被挤下线 + */ + public boolean isForceLogoutByOtherDevice(String token) { + try { + Claims claims = parseToken(token); + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String usernameFromJwt = (String) claims.get(Constants.JWT_USERNAME); + + if (StringUtils.isNotEmpty(usernameFromJwt)) { + String mappedUuid = redisCache.getCacheObject(getUserTokenKey(usernameFromJwt)); + // 如果用户名映射存在且指向其他uuid,说明是被挤下线 + return StringUtils.isNotEmpty(mappedUuid) && !uuid.equals(mappedUuid); + } + return false; + } catch (Exception e) { + log.error("检查是否被挤下线异常'{}'", e.getMessage()); + return false; + } + } +} \ No newline at end of file