From 937cbb6bbd14d3827db6974b75418e03dce4615f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cwuzhen=E2=80=9D?= Date: Mon, 26 Oct 2026 15:49:09 +0800 Subject: [PATCH] =?UTF-8?q?fix(security):=20=E7=99=BB=E5=BD=95=E6=B8=85?= =?UTF-8?q?=E7=90=86=E6=97=A7=E4=BC=9A=E8=AF=9D=E6=97=B6=E5=AE=B9=E5=BF=8D?= =?UTF-8?q?=20JWT=20=E8=A7=A3=E6=9E=90=E5=A4=B1=E8=B4=A5=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E6=97=A0=E6=B3=95=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit logoutByMaxLoginCount 会回调 doLogout,旧随机密钥签发的 token 导致 getExtra 抛异常并打断整个登录。 Confidence: high Scope-risk: narrow Co-authored-by: Cursor --- .../web/listener/UserActionListener.java | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/backend/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java b/backend/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java index ba1e88d4..deca6db2 100644 --- a/backend/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java +++ b/backend/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java @@ -76,15 +76,32 @@ public class UserActionListener implements SaTokenListener { log.info("user doLogin, userId:{}", loginId); } + /** + * 清理在线用户缓存。JWT 密钥变更后旧 token 无法 getExtra,绝不能抛异常打断登录流程 + * (登录时 logoutByMaxLoginCount 会回调 doLogout/doReplaced)。 + */ + private void clearOnlineCache(Object loginId, String tokenValue) { + try { + String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); + TenantHelper.dynamic(tenantId, () -> + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue)); + } catch (Exception e) { + log.warn("清理在线缓存时 token 无法解析(多为 JWT 密钥变更后的旧会话), userId={}, err={}", + loginId, e.getMessage()); + try { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + } catch (Exception ignored) { + // ignore + } + } + } + /** * 每次注销时触发 */ @Override public void doLogout(String loginType, Object loginId, String tokenValue) { - String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); - TenantHelper.dynamic(tenantId, () -> { - RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); - }); + clearOnlineCache(loginId, tokenValue); log.info("user doLogout, userId:{}", loginId); } @@ -93,10 +110,7 @@ public class UserActionListener implements SaTokenListener { */ @Override public void doKickout(String loginType, Object loginId, String tokenValue) { - String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); - TenantHelper.dynamic(tenantId, () -> { - RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); - }); + clearOnlineCache(loginId, tokenValue); log.info("user doKickout, userId:{}", loginId); } @@ -105,10 +119,7 @@ public class UserActionListener implements SaTokenListener { */ @Override public void doReplaced(String loginType, Object loginId, String tokenValue) { - String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); - TenantHelper.dynamic(tenantId, () -> { - RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); - }); + clearOnlineCache(loginId, tokenValue); log.info("user doReplaced, userId:{}", loginId); }