From 24a1d7f745c6a1297ec0831116ea707ef6414833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cwuzhen=E2=80=9D?= Date: Mon, 26 Oct 2026 15:43:59 +0800 Subject: [PATCH] =?UTF-8?q?fix(security):=20JWT=20=E7=AD=BE=E5=90=8D?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E6=8C=89=E6=9C=AA=E7=99=BB=E5=BD=95=E5=A4=84?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E9=81=BF=E5=85=8D=E9=A1=B5=E9=9D=A2=E6=8A=A5?= =?UTF-8?q?=E6=9C=AA=E7=9F=A5=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 密钥切换后残留 token 会在 getExtra 抛 SaJwtException,现统一返回 401 引导重新登录。 Confidence: high Scope-risk: narrow Co-authored-by: Cursor --- .../satoken/handler/SaTokenExceptionHandler.java | 11 +++++++++++ .../common/security/config/SecurityConfig.java | 12 +++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/backend/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java b/backend/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java index a45af89d..db8b18f4 100644 --- a/backend/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java +++ b/backend/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java @@ -3,6 +3,7 @@ package org.dromara.common.satoken.handler; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotPermissionException; import cn.dev33.satoken.exception.NotRoleException; +import cn.dev33.satoken.jwt.exception.SaJwtException; import cn.hutool.http.HttpStatus; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; @@ -49,4 +50,14 @@ public class SaTokenExceptionHandler { return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"); } + /** + * JWT 签名无效/解析失败(常见于密钥变更后残留旧 token) + */ + @ExceptionHandler(SaJwtException.class) + public R handleSaJwtException(SaJwtException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',JWT校验失败'{}',无法访问系统资源", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "登录状态已失效,请重新登录"); + } + } diff --git a/backend/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java b/backend/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java index 21f2c113..39b6dbfd 100644 --- a/backend/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java +++ b/backend/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java @@ -59,9 +59,19 @@ public class SecurityConfig implements WebMvcConfigurer { StpUtil.checkLogin(); // 检查 header 与 param 里的 clientid 与 token 里的是否一致 + // jwt 密钥切换后旧 token 会在 getExtra 时抛 SaJwtException,转为未登录避免页面报“未知异常” String headerCid = request.getHeader(LoginHelper.CLIENT_KEY); String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); - String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); + Object clientIdExtra; + try { + clientIdExtra = StpUtil.getExtra(LoginHelper.CLIENT_KEY); + } catch (Exception e) { + throw NotLoginException.newInstance(StpUtil.getLoginType(), + NotLoginException.INVALID_TOKEN, + "token无效或已过期,请重新登录", + StpUtil.getTokenValue()); + } + String clientId = clientIdExtra == null ? null : clientIdExtra.toString(); if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { // token 无效 throw NotLoginException.newInstance(StpUtil.getLoginType(),