Closed
Description
使用版本:
使用的版本是:
1.42.0
在POM中引入了:
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.42.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.42.0</version>
</dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>1.42.0</version>
</dependency>
涉及的功能模块:
拦截器、异常拦截
测试步骤:
1、在SpringMvcConfig
中配置拦截器:
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器 定义详细认证规则
registry.addInterceptor(new SaInterceptor(handler -> {
// 指定一条 match 规则
SaRouter
// 拦截的 path 列表
.match("/**")
// 排除掉的 path 列表
.notMatch("/401")
.notMatch("/doc.html")
// 要执行的校验动作
.check(CustomSaTokenAuthenticator::check);
SaRouter.match("/wechatApp/unbinding", CustomSaTokenAuthenticator::check);
SaRouter.match("/wechatApp/genBindingQRCode", CustomSaTokenAuthenticator::check);
})).addPathPatterns("/**");
}
其中CustomSaTokenAuthenticator
是自定义的校验逻辑,在其中调用了StpUtil.getSession()
方法。直接获取当前用户信息如果获取不到就视为没登录。
2、正常登录系统
3、访问不存在的URL时,控制台输出:
18:06:26.751 [http-nio-8888-exec-4] WARN [com.lyd.mes.controller.ExceptionController ] 请求(http://localhost:8888/api/error)出现Sa-Token异常
cn.dev33.satoken.exception.SaTokenContextException: SaTokenContext 上下文尚未初始化
at cn.dev33.satoken.context.SaTokenContextForThreadLocalStaff.getModelBox(SaTokenContextForThreadLocalStaff.java:73)
at cn.dev33.satoken.context.SaTokenContextForThreadLocal.getModelBox(SaTokenContextForThreadLocal.java:55)
at cn.dev33.satoken.context.SaTokenContext.getRequest(SaTokenContext.java:66)
at cn.dev33.satoken.context.SaHolder.getRequest(SaHolder.java:49)
at cn.dev33.satoken.router.SaRouter.isMatchCurrURI(SaRouter.java:141)
at cn.dev33.satoken.router.SaRouterStaff.match(SaRouterStaff.java:74)
at cn.dev33.satoken.router.SaRouter.match(SaRouter.java:172)
at com.lyd.mes.config.SpringMvcConfig.lambda$addInterceptors$0(SpringMvcConfig.java:46)
4、发现请求地址和实际地址不符/api/error
发现原来是SpringBoot重定向了错误地址,所以在拦截器中添加忽略
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器 定义详细认证规则
registry.addInterceptor(new SaInterceptor(handler -> {
// 指定一条 match 规则
SaRouter
// 拦截的 path 列表
.match("/**")
// 排除掉的 path 列表
.notMatch("/401")
.notMatch("/doc.html")
// 要执行的校验动作
.check(CustomSaTokenAuthenticator::check);
SaRouter.match("/wechatApp/unbinding", CustomSaTokenAuthenticator::check);
SaRouter.match("/wechatApp/genBindingQRCode", CustomSaTokenAuthenticator::check);
})).addPathPatterns("/**").excludePathPatterns("/error");
}
5、再次访问不存在的URL时,后台无日志打印,请求返回正常:
{
"timestamp": "2025-04-23 18:15:04",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/api/test-not-found"
}
6、主动注销登录,模拟未登录场景
7、访问不存在的URL时,控制台输出:
18:12:13.738 [http-nio-8888-exec-8] ERROR [org.apache.juli.logging.DirectJDKLog ] Servlet.service() for servlet [dispatcherServlet] in context with path [/api] threw exception [Request processing failed; nested exception is cn.dev33.satoken.exception.NotLoginException: 未能读取到有效 token] with root cause
cn.dev33.satoken.exception.NotLoginException: 未能读取到有效 token
at cn.dev33.satoken.exception.NotLoginException.newInstance(NotLoginException.java:134)
at cn.dev33.satoken.stp.StpLogic.getLoginId(StpLogic.java:1085)
at cn.dev33.satoken.stp.StpLogic.getSession(StpLogic.java:1443)
at cn.dev33.satoken.stp.StpLogic.getSession(StpLogic.java:1452)
at cn.dev33.satoken.stp.StpUtil.getSession(StpUtil.java:626)
at com.lyd.mes.util.MesRequestHolder.getCurrentUser(MesRequestHolder.java:36)
at com.lyd.mes.common.auth.CustomSaTokenAuthenticator.check(CustomSaTokenAuthenticator.java:25)
但是!该NotLoginException
异常无法被RestControllerAdvice
拦截处理。
前台收到的响应是:
{
"timestamp": "2025-04-23 18:28:31",
"status": 500,
"error": "Internal Server Error",
"trace": "cn.dev33.satoken.exception.NotLoginException: token 无效:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiIxIiwicm5TdHIiOiJoRnB0Tm9hV0hYMXFWYUJXRWRLcFRYZlNucjlDdnpRTyJ9.t3_cYUP4Vi1H9iQKNWJFJo6AuHEL2enin5ljuPUSC2U\r\n\tat cn.dev33.satoken.exception.NotLoginException.newInstance(NotLoginException.java:134)\r\n\tat cn.dev33.satoken.stp.StpLogic.getLoginId(StpLogic.java:1091)\r\n\tat cn.dev33.satoken.stp.StpLogic.getSession(StpLogic.java:1443)\r\n\tat cn.dev33.satoken.stp.StpLogic.getSession(StpLogic.java:1452)\r\n\tat cn.dev33.satoken.stp.StpUtil.getSession(StpUtil.java:626)\r\n\tat (省略)",
"message": "token 无效:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiIxIiwicm5TdHIiOiJoRnB0Tm9hV0hYMXFWYUJXRWRLcFRYZlNucjlDdnpRTyJ9.t3_cYUP4Vi1H9iQKNWJFJo6AuHEL2enin5ljuPUSC2U",
"path": "(省略)"
}
7、正常登录系统
8、再次访问不存在的URL时,后台无日志打印,请求返回:
{
"timestamp": "2025-04-23 18:15:04",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/api/test-not-found"
}
9、我尝试过把配置改成过滤器,这样可以解决以上的问题,但这样就无法使用RestControllerAdvice
拦截异常了,必须要重写一遍异常处理,或者有什么更好的办法吗?
我的疑问是:
请问,是我的配置不对吗?
在旧版中,就算拦截器中不配置.excludePathPatterns("/error")
也能正常返回404 Not Found
响应,猜测应该是重构了上下文的原因。
访问不存在的URL时,按照预期:
1、如果未登录,应该是要被RestControllerAdvice
拦截并处理。
2、如果已登录,应该是正常返回404 Not Found
响应或被RestControllerAdvice
拦截并处理。
Activity
[-]访问不存在的URL时,无法捕获NotLoginException异常[/-][+]访问不存在的URL时,报错上下文尚末初始化[/+]click33 commentedon May 9, 2025
我在本地未能复现此问题,你的SpringBoot版本用的多少?
click33 commentedon May 9, 2025
如果还有疑问,请联系官网的 sa-token 小助手进行咨询,github 的 issue 我邮箱收不到信息就忽略了
JIANGTUNAN commentedon May 10, 2025
@click33 你好,SpringBoot的版本是
2.6.6
。我创建了个仓库并复现了这个场景,请参考我的仓库。click33 commentedon May 10, 2025
已将解决方案添加至官网报错速查,感感谢提交案例:
https://sa-token.cc/doc.html#/more/common-questions?id=q%ef%bc%9a%e6%8a%a5%e9%94%99%ef%bc%9asatokencontext-%e4%b8%8a%e4%b8%8b%e6%96%87%e5%b0%9a%e6%9c%aa%e5%88%9d%e5%a7%8b%e5%8c%96