Sa-Token v1.46.0 版本特性深度解析:从注销前侦听器到 JSON 反序列化安全加固
【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token
本篇文章以 Sa-Token 仓库根目录下的 update-log.md 更新日志为主体骨架,结合 sa-token-core、sa-token-plugin 等模块的源码实现,系统梳理 v1.46.0 及同期版本的核心技术演进:注销前侦听器、JSON 多态反序列化白名单、HTTP 请求扩展插件、Redis/Redisson 数据访问层增强、SSO 安全加固等。读完本文,你将理解这些特性的设计动机、底层调用链与迁移配置要点,可直接用于生产环境升级评估。
一、更新日志概览与版本脉络
update-log.md是 Sa-Token 仓库的版本变更总账,记录了从 v1.45.0 发布到 v1.46.0 之间每一个功能迭代、安全修复与文档优化节点。从内容结构上看,日志主要分为三大类:
- 核心框架特性:如注销前侦听器、JSON 安全策略、配置项新增等,落点在
sa-token-core; - 插件与集成增强:如 HTTP 请求插件、alone-redisson 独立连接、Redis 序列化改进等,落点在
sa-token-plugin与sa-token-starter; - 工程与生态维护:博客系统、多语言支持、文档站点、示例工程等。
其中v1.46.0(2026-08-18 起)是信息密度最高的版本,本文将以该版本为核心展开,同时覆盖 v1.45.0 的关键能力。
二、注销前侦听器:doBeforeLogout / doBeforeKickout / doBeforeReplaced
2.1 功能定位
v1.46.0 新增了三个"注销前"侦听器事件,用于在账号注销、被顶下线、被踢下线真正生效之前同步触发一次回调。这是对既有全局侦听器体系的重要补充。
在 SaTokenListener.java 中,三个新方法均为default 方法(默认空实现),因此对老用户完全兼容,无需强制改造:
// 注销前 default void doBeforeLogout(String loginType, Object loginId, String tokenValue, SaLogoutParameter logoutParameter) {} // 被踢下线前 default void doBeforeKickout(String loginType, Object loginId, String tokenValue, SaLogoutParameter logoutParameter) {} // 被顶下线前 default void doBeforeReplaced(String loginType, Object loginId, String tokenValue, SaLogoutParameter logoutParameter) {}与之对应,原本的doLogout/doKickout/doReplaced仍然保留,作为"注销后"事件继续生效。
2.2 底层触发机制
从源码调用链看,三个"前"事件统一由 StpLogic.java 中的私有方法_fireBeforeLogoutEvent负责分发,根据SaLogoutParameter.getMode()决定触发哪个事件:
private void _fireBeforeLogoutEvent(Object loginId, String tokenValue, SaLogoutParameter logoutParameter) { if(logoutParameter.getMode() == SaLogoutMode.LOGOUT) { SaTokenEventCenter.doBeforeLogout(loginType, loginId, tokenValue, logoutParameter); } if(logoutParameter.getMode() == SaLogoutMode.KICKOUT) { SaTokenEventCenter.doBeforeKickout(loginType, loginId, tokenValue, logoutParameter); } if(logoutParameter.getMode() == SaLogoutMode.REPLACED) { SaTokenEventCenter.doBeforeReplaced(loginType, loginId, tokenValue, logoutParameter); } }事件最终通过 SaTokenEventCenter.java 广播给所有注册的SaTokenListener实现。SaLogoutParameter携带了登录类型、登录账号、token 值与注销模式,回调中可据此区分场景(如注销时做资源清理、被踢时推送通知、被顶时记录审计日志)。
2.3 使用建议
- 需要在会话销毁前执行清理操作(如解绑设备、下线通知、操作日志)时,优先使用这三个"前"事件;
- 默认方法的设计意味着:只需在你自己的
SaTokenListener实现中重写所需方法即可,其余方法保持空实现,避免样板代码。
三、JSON 安全加固:SaJsonStrategy 类型白名单
3.1 背景:Jackson DefaultTyping 反序列化风险
v1.46.0 修复了一个高危安全漏洞:JacksonDefaultTyping多态反序列化存在 RCE 风险。原因在于,启用默认类型后,序列化字符串中会携带类型信息(如@class),攻击者若可篡改数据源,就能利用类型标记实例化 classpath 上的任意类,触发反序列化攻击链。
修复方案是引入SaJsonStrategy全局类型白名单(核心类见 SaJsonStrategy.java),只有白名单内的类型才允许参与多态反序列化。
3.2 白名单构成
getSaJsonAllowTypeList()每次调用都会现场合并四类来源并返回快照,去重后保持注册顺序:
- JDK 内置允许类型(
getJdkAllowTypeList):集合结构锚点Map/Collection,基本类型包装类Number/Boolean/Character,旧版日期Date/Calendar,以及java.time全家桶(LocalDateTime、Instant、ZonedDateTime等)、BigDecimal/BigInteger、UUID、Enum等常见值类型; - SPI 文件声明(
loadSpiAllowTypeList):读取META-INF/satoken/sa-json-type.list中的类名列表(空行与#开头的注释行会被忽略),便于各插件声明自己的允许类型; SaJsonType标记接口:所有实现了该标记接口的类均被允许;- 用户自定义注册(
registerAllowType(Class)):在初始化前手动注册。
3.3 关键约束与警告
从SaJsonStrategy源码可以看出两个必须注意的约束:
- 注册时机:
isInit()为true(即getSaJsonAllowTypeList()已被调用过)之后,registerAllowType(Class)会抛出SaTokenException。因此自定义类型注册必须在 JSON 插件初始化之前完成——比如在main方法或 Spring 启动前调用; - 强烈不建议注册
Object.class:注册Object.class等同于放开全部类型限制。源码会向System.err输出郑重安全警告,明确提示"@class/@type等类型标记可被用来实例化 classpath 上任意类型",生产环境强烈不建议这么做。
3.4 Jackson 插件的落地方式
在 SaJsonTemplateForJackson.java 的构造器中,插件通过buildAllowTypeValidator()依据SaJsonStrategy.instance.getSaJsonAllowTypeList()构建PolymorphicTypeValidator,再配合activateDefaultTyping启用默认类型——即序列化时嵌入类型信息(@class属性),反序列化时仅接受白名单内的类型。
同一批安全更新还包含:
- Snack3 序列化不再写入类型信息,对齐 fastjson 的安全策略;
- JSON 反序列化容错优化(参考
sa-token-serializer-features等模块),提升不同数据源下的健壮性。
四、HTTP 请求扩展插件:SaHttpTemplate 的统一抽象
4.1 新增两个实现
v1.46.0 新增了sa-token-rest-client与sa-token-rest-template两个 HTTP 请求插件,分别基于 Spring 6.1+ 的RestClient与经典RestTemplate实现统一接口SaHttpTemplate(接口定义见 SaHttpTemplate.java),配套文档见 plugin/http-extend.md。
以 SaHttpTemplateForRestClient.java 为例,内部持有RestClient.create(),实现get(url)与postByFormData(url, params)两个方法,其中 POST 以application/x-www-form-urlencoded提交表单数据,并统一通过SaManager.log输出 debug 级别的请求/响应日志,便于排查 SSO 等远程交互链路:
public class SaHttpTemplateForRestClient implements SaHttpTemplate { private final RestClient restClient = RestClient.create(); @Override public String get(String url) { SaManager.log.debug("发起请求,GET:{}", url); String res = restClient.get().uri(url).retrieve().body(String.class); SaManager.log.debug("返回结果:{}", res); return res; } // postByFormData(url, params):以表单格式 POST 并返回字符串结果 }4.2 使用场景
该抽象主要服务于SSO 客户端与认证服务器的 HTTP 通信,为后续扩展更多底层 HTTP 客户端(如 OkHttp、Forest 等)提供统一插槽,方便接入方按自身技术栈替换实现。
五、Redis / Redisson 数据层增强
v1.46.0 对 Redis 数据访问层做了多项修复与增强,直接影响使用 Redis 存储会话的稳定性。
5.1 TTL 保持:SET KEEPTTL
sa-token-redis-template及 jdk-serializer 版本修复了update key 时 TTL 偏移的问题(issues #I80P5O、#ICWJOQ)。根因是旧的更新操作会重置过期时间,导致 Token 被意外"续命"。- 修复后,SaTokenDaoForRedisTemplate.java 的 update 方法使用Redis
SET KEEPTTL(Expiration.keepTtl())原子保留原 TTL,前提是Redis 版本 >= 6.0; - 对于Redis 6.0 以下版本,文档补充了替代方案:将 update 方法替换为"毫秒级 TTL 写回"写法。相关说明同步到了 more/common-questions.md 与 up/integ-redis.md,低版本用户按文档替换即可规避报错。
5.2 searchData 由 KEYS 改为 SCAN
redis-template 的searchData从KEYS命令改为SCAN游标方式(ScanOptions+connection.scan),既避免阻塞主线程,也兼容低版本 Spring Data Redis(参考 issue #702)。源码中wrapKey也被抽为可重写方法,开发者可以自定义键前缀策略。
5.3 Redisson Dao 增强
SaTokenDaoForRedisson.java 的改进点:
- 支持指定 Codec:提供
SaTokenDaoForRedisson(RedissonClient, Codec)构造器,默认使用StringCodec.INSTANCE,与业务方 RedissonClient 的全局 Codec 隔离,避免序列化器互相干扰; - update 使用
setAndKeepTTL:基于RBucket.setAndKeepTTL()原子保留原过期时间(同样要求 Redis >= 6.0,低版本有毫秒写法替代); - 同时补充了按配置决定是否启用 Redis的示例(参考 issue #I9I4ZG),方便多环境切换。
5.4 sa-token-alone-redisson 独立连接插件
v1.46.0 新增sa-token-alone-redisson插件(文档见 plugin/alone-redisson.md,示例工程见sa-token-demo-alone-redisson等目录),与既有sa-token-alone-redis插件形成互补,用于让权限缓存与业务数据缓存使用独立的 Redis 连接。
从 SaAloneRedissonRegister.java 的源码结构看,其核心逻辑是:读取独立连接配置SaAloneRedissonProperties,基于独立的RedissonClient构建new SaTokenDaoForRedisson(aloneClient)并注册为SaTokenDaoBean,从而覆盖业务 RedissonClient 对应的 Dao,实现权限数据与业务数据的连接级隔离。测试用例位于sa-token-plugin/sa-token-alone-redisson/src/test/下,含纯单测与 Spring 环境单测两个场景。
六、Session 类型安全集合读取
v1.46.0 为 Session 数据读取新增了类型安全集合 API:getList/getSet/getMap,定义在 SaGetValueInterface.java(接口被SaSession继承使用):
default <T> List<T> getList(String key, Class<T> elementClass) { ... } default <T> Set<T> getSet(String key, Class<T> elementClass) { ... } default <K, V> Map<K, V> getMap(String key, Class<K> keyClass, Class<V> valueClass) { ... }调用时传入元素/键值的具体 Class,读取时自动完成反序列化与泛型转换,避免手工List<?>强转带来的ClassCastException。对应的SaSetValueInterface还提供了带兜底函数的三参重载(如getList(key, elementClass, fun)),当值为 null 时执行fun获取默认值并写入,配合 JSON 白名单机制,可安全存取带类型信息的复杂对象。
七、SSO 单点登录安全与功能增强
v1.46.0 对 SSO 模块做了集中加固:
- 指定账号单点注销:SSO 客户端新增"指定账号单点注销 API",可精确注销某个账号而非全部会话;
StpInterface.isDisabled补充loginType参数:方法签名从isDisabled(loginId, service)演进为isDisabled(loginId, service, loginType)(见 StpInterface.java),使"账号封禁"判断可以按登录类型区分,StpLogic.isDisableLevel 内部即通过SaManager.getStpInterface().isDisabled(loginId, service, loginType)完成查询;buildServerAuthUrl健壮性增强:识别 URL 编码后的back参数,back为空时跳过追加,并强化重定向编码/解码,防止参数被截断或二次编码错乱;- NoSdk 模式签名校验强化:demo 的签名校验补充时间窗与nonce 防重放,配合
SsoSignUtil(该工具在 v1.45.0 的 NoSdk 补全中引入,文档见 sso/sso-nosdk.md),提升无 SDK 集成场景的抗重放能力; - 安全漏洞修复:OAuth2/SSO 的
redirect参数绕过 allow-url 校验漏洞已修复,重定向目标必须通过域名白名单校验。
八、安全与健壮性:多项底层增强
v1.46.0 还包含一批跨模块的安全与健壮性改进:
allowLoginIdColon配置项:默认false,禁止 loginId 包含冒号(:)。该校验位于 StpLogic.checkLoginArgs:
// 3、账号 id 不能包含冒号(:),除非配置 allowLoginIdColon=true if( ! Boolean.TRUE.equals(getConfigOrGlobal().getAllowLoginIdColon()) && id.toString().contains(":")) { throw new SaTokenException("loginId 不能包含冒号(:)").setCode(SaErrorCode.CODE_11018); }配置项定义在 SaTokenConfig.java,默认值false。之所以限制冒号,是因为 token 解析与 Session 存储中冒号通常作为分隔符,允许冒号可能引入歧义;确有特殊账号需要包含冒号时,可显式配置allowLoginIdColon=true放开(有校验与告警机制兜底,需自行评估风险)。
- 随机字符串/数字改用 CSPRNG:token 随机串生成从普通随机源切换为
SecureRandom,降低 token 被预测的概率; - JWT extraData 禁止包含保留字段:防止扩展数据覆盖 JWT 标准声明,见
sa-token-jwt插件; SaRequest/SaResponse/SaStorage创建策略:通过SaStrategy中的SaCreateSaRequestFunction/SaCreateSaResponseFunction/SaCreateSaStorageFunction(SaStrategy.java)允许自定义请求/响应/存储对象的创建方式,便于适配非标准 Web 容器(参考 issue #841);- Context Filter 注册 REQUEST+ASYNC:修复 SSE/Flux 流式返回场景下上下文未初始化的缺陷(issues #IC4XFE、#ICB9OJ);
getSaTokenConfig策略:SaStrategy新增getSaTokenConfig函数(默认为 null),启用后接管SaManager.getConfig()的返回值,支持从外部(如配置中心)动态提供配置来源;isLastingCookie为 null 时getCookieTimeout不再 NPE:修复 Cookie 超时计算空指针;- WebFlux/Gateway 兼容:修复 Boot3+ WebFlux/Gateway 下
setStatus的NoSuchMethodError(reactor starter 结构调整,issues #IIAW1A、#916、#IHRIBY); isUrl支持 IPv6 方括号地址并拒绝裸 IPv6,sa-token-dependencies不再锁定 Reactor 版本,避免与 Spring Cloud Gateway 冲突(issue #622)。
九、如何获取与验证这些特性
9.1 版本与依赖
- 框架版本号已升级至1.46.0,可通过 Maven 中央仓库引入(javadoc/sources/gpg 仅在发布 Maven 中央时启用);
- BOM 模块补齐了
sa-token-caffeine与sa-token-loveqq-boot-starter,统一依赖版本管理可参考 sa-token-bom/pom.xml 与 sa-token-dependencies/pom.xml。
9.2 示例工程对照
更新日志中多次提到的特性都有对应示例与测试可对照学习:
- HTTP 插件:
sa-token-plugin/sa-token-rest-client、sa-token-plugin/sa-token-rest-template源码及测试; - SSO 增强:
sa-token-plugin/sa-token-sso源码,配合 sso/sso-apidoc.md 与 sso/sso-nosdk.md 文档; - Redisson 独立连接:
sa-token-plugin/sa-token-alone-redisson及sa-token-demo-alone-redisson/sa-token-demo-alone-redisson-sb4示例; - Redis 数据层:
sa-token-plugin/sa-token-redis-template下的SaTokenDaoForRedisTemplateSearchDataTest等测试验证了 SCAN 搜索行为; - JSON 安全:
sa-token-plugin/sa-token-jackson/sa-token-jackson3/sa-token-snack3/sa-token-fastjson插件源码与测试,以及 plugin/custom-serializer.md 文档。
9.3 升级注意事项(速查清单)
| 关注点 | 说明 |
|---|---|
| Redis 版本 | update 方法依赖SET KEEPTTL,Redis >= 6.0可无缝使用;低版本需替换为毫秒 TTL 写法(见 common-questions.md / integ-redis.md) |
| JSON 白名单 | 自定义反序列化类型需在 JSON 插件初始化前通过SaJsonStrategy.registerAllowType注册;切勿注册Object.class放开限制 |
| loginId 冒号 | 默认禁止:,特殊账号需显式开启allowLoginIdColon |
| SSO 回调 | back参数现在支持 URL 编码形式,重定向会统一编解码,老链路如有手工编码需对齐新行为 |
| WebFlux | Boot3+ 请使用新版 reactor starter 结构,setStatus不再冲突 |
十、总结
Sa-Token v1.46.0 是一次"安全加固 + 集成扩展"并重的版本:一方面通过 JSON 类型白名单、CSPRNG 随机源、redirect 校验等手段系统性收紧了框架的安全边界;另一方面通过注销前侦听器、类型安全集合 API、HTTP 请求抽象、Redisson 独立连接等能力,扩展了框架在多会话管理、分布式存储与异构集成场景下的可用性。对于升级用户而言,重点关注 Redis 版本兼容与 JSON 白名单注册时机两个前置条件,即可平滑迁移。本文对应的完整变更记录可在仓库根目录的 update-log.md 中持续跟踪。
【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考