sa-token/sa-token-doc/doc/more/common-questions.md
2022-05-06 10:31:46 +08:00

11 KiB
Raw Blame History

常见问题排查

本篇整理大家在群聊里经常提问的一些问题如有补充欢迎提交pr

toc


一、常见报错

报错非Web上下文无法获取Request

报错原因Sa-Token 的部分 API 只能在 Web 上下文中调用,报这个错说明你调用 Sa-Token 的地方不在 Web 上下文中,请排查:

  1. 是否在 main 方法中调用了 Sa-Token 的API
  2. 是否在带有 @Async 注解的方法中调用了 Sa-Token 的API
  3. 是否在一些丢失web上下文的子线程中调用了 Sa-Token 的API例如 MyBatis-PlusinsertFill 自动填充
  4. 是否在一些非 Http 协议的 RPC 框架中(例如 Dubbo调用了 Sa-Token 的API
  5. 是否在 SpringBoot 启动初始化的方法中调用了 Sa-Token 的API例如@PostConstruct

解决方案:先获取你想要的值,再把这个值当做一个参数传递到这些方法中,而不是直接从方法内调用 Sa-Token 的API。

报错:未初始化任何有效上下文处理器?

报错原因Sa-Token底层不能确认最终运行的web容器所以抽象了 SaTokenContext 接口,对接不同容器时需要注入不同的实现,通常这个注入工作都是框架自动完成的, 你只需要按照文档开始部分集成相应的依赖即可

如果报了这个错误,说明框架没有注入正确的上下文实现,请排查:

  1. 如果你的项目是微服务项目,请直接参考:微服务-依赖引入说明,如果是单体项目,请往下看:
  2. 请判断你的项目是 SpringMVC 环境还是 WebFlux 环境
    • 如果是 SpringMVC 环境就引入 sa-token-spring-boot-starter 依赖,参考:在SpringBoot环境集成
    • 如果是 WebFlux 环境就引入 sa-token-reactor-spring-boot-starter 依赖,参考:在WebFlux环境集成
    • 引入错误的依赖会导致SaTokenContext初始化失败,抛出上述异常
    • 如果你还无法分辨你是哪个环境,就看你的 pom.xml 依赖,如果引入了spring-boot-starter-web就是SpringMVC环境如果引入了 spring-boot-starter-webflux 就是WebFlux环境。……什么你说你两个都引入了那你的项目能启动成功吗
    • 你说你两个包都没引入?那你为什么不引入一个呢?
  3. 如果是 WebFlux 环境而且正确引入了依赖,依然报错,请检查是否注册了全局过滤器,在 WebFlux 下这一步是必须的。
  4. 如果以上步骤排除无误后依然报错,请直接提 issues 或者加入QQ群求助。

报错NotLoginExceptionxxx

这个错是说明调用接口的人没有通过登录认证,请注意通常异常提示语已经描述清楚了没有通过认证的具体原因例如没有提供Token、提供的Token是无效的、提供的Token已经过期……等等

请根据异常提示语以及报错位置进行排查,可参考:NotLoginException 场景值

加了注解进行鉴权认证,不生效?

注解鉴权功能默认关闭两种方式任选其一进行打开注册注解拦截器、集成AOP模块参考注解式鉴权 如果已经打开仍然没有效果,加群说明一下复现步骤

有时候我不加 Token 也可以通过鉴权,请问是怎么回事?

可能是Cookie帮你自动传了在浏览器或 Postman 中会自动维护Cookie模式如不需要可以在配置文件is-read-cookie: false,然后重启项目再测试一下

一个User对象存进Session后再取出来时报错无法从User类型转换成User类型

群员亲测,当你打开热部署模式后,先存进去的对象,热刷新后再取出,会报错,关闭热刷新即可解决

Springboot环境下采用自定义拦截器排除了某个路径仍然被拦截了

可能是404了SpringBoot环境下如果访问接口404后会被转发到/error,然后被再次拦截。

如果不是404可以先打印一下访问的路由因为后端拦截的未必是你前端访问的这个path先获取到具体path再仔细分析。

我配置了 active-timeout 值,但是当我每次续签时 Redis 中的 ttl 并没有更新,是不是 bug 了?

不更新是正常现象,active-timeout不是根据 ttl 计算的是根据value值计算的value 记录的是该 Token 最后访问系统的时间戳, 每次验签时用:当前时间 - 时间戳 > active-timeout来判断这个 Token 是否已经超时

集成 Redis 后,明明 Redis 中有值却还是提示无效Token

根据以往的处理经验,发生这种情况 90% 的概率是因为你找错了Redis代码连接的Redis和你用管理工具看到的Redis并不是同一个。

你可能会问:我看配置文件明明是同一个啊?

我的回答是:别光看配置文件,不一定准确,在启动时直接执行 SaManager.getSaTokenDao().set("name", "value", 100000); 随便写入一个值看看能不能根据你的预期写进这个Redis如果能的才能证明 Redis 连接没问题,再进行下一步排查。

整合 Redis 时先选择了默认jdk序列化后又改成 jackson 序列化程序开始报错SerializationException

两者的序列化算法不一致导致的反序列化失败,如果要更改序列化方式,则需要先将 Redis 中历史数据清除,再做更新

集成 jwt 后为什么在 getSession 时提示 jwt has not session ?

jwt 的招牌便是无须借助服务端完成会话管理,如果集成jwt后再使用Session功能,那将又回到了传统Session模式,属于自断招牌,此种技术组合没有意义, 因此jwt集成模式不提供Session功能,如果需要Session功能,就不要集成jwt

我加了 Sa-Token 的全局过滤器,浏览器报错跨域了怎么办?

参考:https://blog.csdn.net/shengzhang_/article/details/119928794

二、常见疑问

登录方法需要我自己实现吗?

是的,不同于shiro等框架,Sa-Token不会在登录流程中强插一脚,开发者比对完用户的账号和密码之后,只需要调用StpUtil.login(id)通知一下框架即可

框架抛出的权限不足异常,我想根据自定义提示信息,可以吗?

可以,在全局异常拦截器里捕获NotPermissionException,可以通过getPermission()获取没有通过认证的权限码,可以据此自定义返回信息

我的项目权限模型不是RBAC模型很复杂可以集成吗

无论什么模型只要能把一个用户具有的所有权限塞到一个List里返回给框架就能集成

当我配置不并发登录时,每次登陆都会产生一个新的 Token旧 Token 依然被保存在 Redis 中,框架为什么不删除呢?

首先,不删除旧 Token 的原因是为了在旧 Token 再次访问系统时提示他:已被顶下线。

而且这个 Token 不会永远留在 Redis 里,在其 TTL 到期后就会自动清除,如果你想让它立即消失,可以:

  • 方法一:配置文件把 is-concurrentis-share 都打开,这样每次登陆都会复用以前的旧 Token就不会有废弃 Token 产生了。
  • 方法二:每次登录前把先调用注销方法,把这个账号的旧登录都给清除了。
  • 方法三写一个定时任务查询Redis值进行删除。

我使用过滤器鉴权 or 全局拦截器鉴权,结果 Swagger 不能访问了,我应该排除哪些地址?

尝试加上排除 "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**" ,"/doc.html/**","/error","/favicon.ico"

不同版本可能会有所不同,其实在前端摁一下 F12 看看哪个 url 报错排除哪个就行了(另附:注解鉴权是不需要排除的,因为 Swagger 本身也没有使用 Sa-Token 的注解)

SaRouter.match 有多个路径需要排除怎么办?

可以点进去源码看一下,SaRouter.match方法有多个重载,可以放一个集合, 例如:
SaRouter.match(/**).notMatch("/login", "/reg").check(r -> StpUtil.checkLogin());

为什么StpUtil.login() 不能直接写入一个User对象

StpUtil.login()只是为了给当前会话做个唯一标记,通常写入UserId即可如果要存储User对象可以使用StpUtil.getSession()获取Session对象进行存储。

前后台分离模式下和普通模式有何不同?

主要是失去了Cookie无法自动化保存和提交token秘钥,可以参考章节:前后台分离

前后台分离时,前端提交的 header 参数是叫 token 还是 satoken 还是 tokenName

默认是satoken如果想换一个名字更改一下配置文件的tokenName即可。

权限可以做成动态的吗?

权限本来就是动态的只有jwt那种模式才是非动态的

我不想让框架自动操作Cookie怎么办

在配置文件将isReadCookie值配置为false

怎么关掉每次启动时的字符画打印?

在配置文件将isPrint值配置为false

StpUtil.getSession()必须登录后才能调用吗?如果我想在用户未登录之前存储一些数据应该怎么办?

StpUtil.getSession()获取的是User-Session必须登录后才能使用如果需要在未登录状态下也使用Session功能请使用Token-Session
步骤:先在配置文件里将tokenSessionCheckLogin配置为false,然后通过StpUtil.getTokenSession()获取Session

我只使用header来传输token还需要打开Cookie模式吗

不需要如果只使用header来传输token可以在配置文件关闭Cookie模式isReadCookie=false

我想让用户修改密码后立即掉线重新登录,应该怎么做?

框架内置 [强制指定账号下线] 的APi在执行修改密码逻辑之后调用此API即可: StpUtil.logout()

代码鉴权、注解鉴权、路由拦截鉴权,我该如何选择?

这个问题没有标准答案,这里只能给你提供一些建议,从鉴权粒度的角度来看:

  1. 路由拦截鉴权:粒度最粗,只能粗略的拦截一个模块进行权限认证
  2. 注解鉴权:粒度较细,可以详细到方法级,比较灵活
  3. 代码鉴权粒度最细不光可以控制到方法级甚至可以if语句决定是否鉴权

So从鉴权粒度的角度来看需要针对一个模块鉴权的时候就用路由拦截鉴权需要控制到方法级的时候就用注解鉴权需要根据条件判断是否鉴权的时候就用代码鉴权

Sa-Token的全局过滤器我应该怎么指定它的优先级呢

为了保证相关组件能够及时初始化,框架默认给过滤器注册的优先级为-100如果你想更改优先级直接在注册过滤器的方法上加上 @Order(xxx) 即可覆盖框架的默认配置

还是有不明白到的地方?

请在giteegithub 提交 issues或者加入qq群交流群链接在首页