Sa-Token JWT 集成实战:sa-token-jwt 插件三种整合模式、Token 结构解析与源码级避坑指南
发布时间:2026/9/13 17:05:26 作者:尧图编辑部 阅读量:1,286

Sa-Token JWT 集成实战sa-token-jwt 插件三种整合模式、Token 结构解析与源码级避坑指南【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-TokenSa-Token 的 JWT 集成并非简单地把 Token 换成 JWT 字符串而是要决定登录态数据到底存在 Redis 里还是存在 Token 里。本篇基于官方文档《和 jwt 集成》原文档并结合 sa-token-jwt 插件源码完整讲解依赖引入、秘钥配置、Simple/Mixin/Stateless 三种模式的注入方式与行为差异、扩展参数注入、多账户模式集成以及自定义签名算法并深入源码说明每个模式为什么这样设计读完即可在 Spring Boot 项目中落地一套安全可控的 JWT 登录态方案。一、插件定位sa-token-jwt 做了什么JWTJSON Web Token本身是一种自包含、可签名校验的 Token 格式。sa-token-jwt 插件模块路径 sa-token-plugin/sa-token-jwt的作用是把 Sa-Token 的登录态体系与 JWT 做整合登录时签发 JWT 风格的 Token鉴权时通过校验签名和载荷来还原登录身份从而让前后端分离场景下可以携带无状态的登录凭证。从插件 pom.xml 可以看到它只依赖两个东西sa-token-coreSa-Token 核心包cn.hutool:hutool-jwt底层 JWT 的创建与解析由 Hutool 的JWT工具类完成。也就是说插件本身是一层薄封装真正的 JWT 编解码交给 HutoolSa-Token 负责的则是把 JWT 的生成/解析结果接到StpLogic的登录、注销、鉴权主流程上。版本兼容性注意事项官方文档给出了两条明确的版本约束sa-token-jwt显式依赖 hutool-jwt保险起见项目中要么不引入 hutool要么引入版本 5.7.14的 hutoolhutool5.8.13 和 5.8.14版本下会出现类型转换问题需要避开这两个版本。从当前仓库的依赖管理文件 sa-token-dependencies/pom.xml 可以看到官方统一管理的hutool-jwt.version为5.8.36即仓库自身使用的就是高于 5.7.14 且避开问题版本的新版本。如果你的项目通过 Maven 引入插件建议同样在依赖管理dependencyManagement中显式锁定一个 5.7.14 且非 5.8.13/5.8.14 的 hutool 版本避免传递依赖与项目内已有 hutool 版本冲突。二、引入依赖在已经引入 Sa-Token 的基础上添加Maven 方式!-- Sa-Token 整合 jwt -- dependency groupIdcn.dev33/groupId artifactIdsa-token-jwt/artifactId version${sa.top.version}/version /dependencyGradle 方式// Sa-Token 整合 jwt implementation cn.dev33:sa-token-jwt:${sa.top.version}${sa.top.version}替换为实际版本号当前仓库源码版本在根 pom.xml 中定义为revision 1.46.0可按需选用对应正式版。三、配置 JWT 秘钥三种 JWT 模式都依赖同一个秘钥做签名未配置时解析/生成会直接抛出请配置 jwt 秘钥异常源码中对应SaJwtErrorCode.CODE_30205见 SaJwtTemplate.java 的parseToken与三个StpLogicJwtForXxx类中的jwtSecretKey()方法。在application.yml中配置yaml 风格sa-token: # jwt秘钥 jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjkproperties 风格# jwt秘钥 sa-token.jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk安全提示请勿直接复制官网示例中的这个字符串随便按几个字符即可生产环境建议使用随机生成的长字符串并且该秘钥属于敏感信息不要提交到代码仓库的明文配置中。从源码看这个配置项映射到核心包的SaTokenConfig字段定义在 SaTokenConfig.java注释写明只有集成 jwt 相关模块时此参数才会生效插件通过getConfigOrGlobal().getJwtSecretKey()逐实例、再回落到全局读取该值。默认签名算法为HS256见 SaJwtTemplate.createSignerpublic JWTSigner createSigner (String keyt) { return JWTSignerUtil.hs256(keyt.getBytes()); }四、注入 JWT 整合实现三选一插件提供了三种整合规则对应三个StpLogic子类你需要选择其中一种注入到项目中同一个 Spring 上下文只注册一个StpLogicBeanSimple 简单模式Token 风格替换Configuration public class SaTokenConfigure { // Sa-Token 整合 jwt (Simple 简单模式) Bean public StpLogic getStpLogicJwt() { return new StpLogicJwtForSimple(); } }Mixin 混入模式混入部分逻辑Configuration public class SaTokenConfigure { // Sa-Token 整合 jwt (Mixin 混入模式) Bean public StpLogic getStpLogicJwt() { return new StpLogicJwtForMixin(); } }Stateless 无状态模式服务器完全无状态Configuration public class SaTokenConfigure { // Sa-Token 整合 jwt (Stateless 无状态模式) Bean public StpLogic getStpLogicJwt() { return new StpLogicJwtForStateless(); } }三个实现类分别位于StpLogicJwtForSimple.javaStpLogicJwtForMixin.javaStpLogicJwtForStateless.java它们都重写了父类StpLogic的createTokenValue方法使生成的 Token 值变为 JWT 字符串但各自重写的其余方法决定了行为差异下面逐一对比。五、三种模式的策略对比含源码依据注入不同模式会让框架具有不同的行为策略。以下是三种模式的差异点官方文档以同时引入 jwt 与 Redis 为前提进行对比功能点Simple 简单模式Mixin 混入模式Stateless 无状态模式Token风格jwt风格jwt风格jwt风格登录数据存储Redis中存储Token中存储Token中存储Session存储Redis中存储Redis中存储无Session注销下线前后端双清数据前后端双清数据前端清除数据踢人下线API支持不支持不支持顶人下线API支持不支持不支持登录认证支持支持支持角色认证支持支持支持权限认证支持支持支持timeout 有效期支持支持支持active-timeout 有效期支持支持不支持id反查Token支持支持不支持会话管理支持部分支持不支持注解鉴权支持支持支持路由拦截鉴权支持支持支持账号封禁支持支持不支持身份切换支持支持支持二级认证支持支持支持模式总结Token风格替换jwt 与 Redis 逻辑混合完全舍弃Redis只用jwt从源码看三种模式的本质区别Simple 模式——只换 Token 外观。StpLogicJwtForSimple 仅重写了createTokenValue且调用的是简单方式的SaJwtUtil.createToken(loginType, loginId, extraData, keyt)——注意这个简单方式不把deviceType和timeout(eff)写进 Token因此 Token 中只有账号身份与扩展数据登录有效期、设备类型等仍然由 Sa-Token 持久层如 Redis维护。这就是为什么 Simple 模式下登录数据存储仍落在 Redis 中踢人/顶人下线等 API 也完全可用。Mixin 模式——身份进 Token状态仍在 Redis。StpLogicJwtForMixin 调用的是全参数方式的createToken会把deviceType和eff13 位到期时间戳timeout-1时表示永不过期写进载荷见 SaJwtTemplate.createToken 中的 eff 计算逻辑。同时getLoginIdNotHandle改为直接从 JWT 载荷解析loginId第 110~121 行过期时错误码 30204返回-3NotLoginException.TOKEN_TIMEOUT以便外层精确报Token 已过期saveTokenToIdMapping、deleteTokenToIdMapping、updateTokenToIdMapping全部改为空操作第 184~201 行——token 与 id 的映射已经内嵌在 JWT 里不再需要持久层记录正因如此replaced顶人下线、_logout、_logoutByTokenValue按指定 token 注销以及searchTokenValue会话搜索都直接抛出ApiDisabledException第 145~155、243~246 行这些 API 在 Mixin 模式下不可用getTokenTimeout改为读取 JWT 里的eff计算剩余时间第 209~211 行getConfigOfMaxTryTimes恒返回-1第 264~267 行isSupportShareToken恒返回false第 255~258 行。Stateless 模式——彻底无 Redis。StpLogicJwtForStateless 重写了createLoginSession整个登录流程只剩三步——校验登录参数、生成 JWT、发布doLogin登录事件全程不写持久层其getSaTokenDao()被重写为直接抛出ApiDisabledException第 216~219 行意味着 Session 存取、id 反查 Token、踢人、封禁检查等一切依赖持久化对象的能力均被禁用。注销也只是前端清除数据logout 仅删除请求级 storage 标记并在开启 Cookie 时删除 Cookie服务端不做任何使 Token 失效的动作——这是 JWT 无状态的天然代价已签发的 Token 在eff到期前无法被服务端主动作废。三种模式各自的单元测试分别见 StpLogicJwtForSimpleTest.java、StpLogicJwtForMixinTest.java、StpLogicJwtForStatelessTest.java可对照源码验证各能力开关。仓库中的完整示例工程为 sa-token-demo/sa-token-demo-jwt可直接运行观察各模式下的 Token 样式与接口行为。选型建议结合上表与源码行为需要保留踢人/顶人/会话管理等完整能力、仅希望 Token 换成 JWT 外观时选 Simple希望 Token 自带到期时间、减少一次 Redis 反查但仍需 Session 时选 Mixin微服务/网关场景要求服务端完全不存储会话、接受注销前端丢弃语义时选 Stateless。六、开始使用登录、鉴权与观察 Token 样式注入任一模式后Sa-Token 的所有原有 API 保持不变/** * 登录测试 */ RestController RequestMapping(/acc/) public class LoginController { // 测试登录 RequestMapping(login) public SaResult login() { StpUtil.login(10001); return SaResult.ok(登录成功); } // 查询登录状态 RequestMapping(isLogin) public SaResult isLogin() { return SaResult.ok(是否登录 StpUtil.isLogin()); } // 测试注销 RequestMapping(logout) public SaResult logout() { StpUtil.logout(); return SaResult.ok(); } }访问上述接口登录成功后返回的 Token 即为标准三段式 JWTeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbklkIjoiMTAwMDEiLCJybiI6IjZYYzgySzBHVWV3Uk5NTTl1dFdjbnpFZFZHTVNYd3JOIn0.F_7fbHsFsDZmckHlGDaBuwDotZwAjZ0HB14DRujQfOQ这段 Token 的 Header 解码后是{typ:JWT,alg:HS256}Payload 包含loginId和rn乱数字段等数据。对照源码中的载荷 Key 定义SaJwtTemplate.java各模式写入的字段为Key含义写入场景loginType账号体系标识默认login三种模式loginId账号 id三种模式deviceType登录设备类型Mixin / Stateless全参数方式eff有效截止期13 位时间戳-1表示永不过期Mixin / StatelessrnStr32 位随机串防止同账号同一毫秒生成的 Token 完全相同三种模式自定义 extra 键业务扩展参数见下节值得注意的是rnStr字段即使同一账号在同一毫秒连续登录两次SaFoxUtil.getRandomString(32)也会保证签出的 Token 不同这正是 Simple 模式下is-share必须恒为 false 的原因详见第九节。解析侧的校验链在 SaJwtTemplate.parseToken 中秘钥非空 → Token 非空且可解析错误码 30201→ 校验签名30202→ 校验loginType匹配30203→ 可选校验eff是否过期30204。所有校验失败统一抛出 SaJwtException错误码定义见 SaJwtErrorCode.java。七、注入扩展参数setExtra你可以通过SaLoginParameter在登录时把业务数据直接写进 JWT 载荷// 登录10001账号并为生成的 Token 追加扩展参数name StpUtil.login(10001, new SaLoginParameter().setExtra(name, zhangsan)); // 连缀写法追加多个 StpUtil.login(10001, new SaLoginParameter() .setExtra(name, zhangsan) .setExtra(age, 18) .setExtra(role, 超级管理员)); // 获取扩展参数 String name StpUtil.getExtra(name); // 获取任意 Token 的扩展参数 String name StpUtil.getExtra(tokenValue, name);三个模式类都把isSupportExtra()重写为true并各自实现getExtraSimple 模式走getPayloadsNotCheck不校验eff便于对已过期 Token 取数Mixin/Stateless 模式走getPayloads完整校验。保留字段限制从源码看loginType、loginId、deviceType、eff、rnStr五个 Key 被声明为RESERVED_PAYLOAD_KEYSSaJwtTemplate.checkExtraData通过setExtra传入这些 Key 会抛出SaJwtException错误码 30207extraData 不可包含保留字段防止业务数据覆盖登录态元信息。容量提示extra 数据会完整编码进 Token 载荷并随每个请求发送因此只适合放少量短字段姓名、角色这类不要塞入大 JSON且 JWT 载荷只签名不加密extra 中不可放密码、密钥等敏感信息。八、多账户模式多账号体系下集成 JWTsa-token-jwt 默认只为StpUtil对应的StpLogic注入StpLogicJwtForXxx实现。如果你在项目中自定义了StpUserUtil多账号体系它不会自动获得 JWT 行为需要手动为其替换StpLogic/** * 为 StpUserUtil 注入 StpLogicJwt 实现 */ PostConstruct public void setUserStpLogic() { StpUserUtil.setStpLogic(new StpLogicJwtForSimple(StpUserUtil.TYPE)); }关键点StpLogicJwtForSimple/StpLogicJwtForMixin/StpLogicJwtForStateless都提供了带loginType参数的构造函数如 StpLogicJwtForSimple 构造器loginType会写入 JWT 的loginType载荷并在解析时校验匹配——这保证不同账号体系的 Token 互不串用。九、自定义 Token 生成算法更换签名方式等如果需要自定义生成 Token 的算法例如把 HS256 换成 RS256 非对称签名直接重写SaJwtTemplate即可。SaJwtUtil 持有一个静态的saJwtTemplate实例并暴露了setSaJwtTemplate入口所有createToken/generateToken/parseToken都是对它的委托因此替换该对象即可全局生效/** * 自定义 SaJwtUtil 生成 token 的算法 */ PostConstruct public void setSaJwtTemplate() { SaJwtUtil.setSaJwtTemplate(new SaJwtTemplate() { Override public String generateToken(JWT jwt, String keyt) { System.out.println(------ 自定义了 token 生成算法); return super.generateToken(jwt, keyt); } }); }若要彻底更换签名算法可以在此基础上重写createSigner(String keyt)返回其他JWTSigner实现parseToken内部同样调用createSigner做验签所以生成与校验两侧会自动保持一致。SaJwtUtil提供的常用静态入口创建、解析、取载荷、取 loginId、算剩余有效期均可参考其类注释单元测试见 SaJwtUtilTest.java 与 SaJwtTemplateTest.java。十、三种模式下的配置注意事项源码印证官方文档明确列出三条硬性约束源码中均有对应实现理解它们可以避免大量配置了却不生效的困惑1. 使用 jwt-simple 模式后is-share恒等于 falseis-sharetrue的语义是同一账号重复登录复用同一个 Token。但 Simple 模式下每次生成 Token 都会写入新的rnStr随机串、并支持setExtra携带个性化数据复用旧 Token会破坏 Extra 数据与随机串的正确性。因此 StpLogicJwtForSimple.isSupportShareToken 直接重写返回false——即使你在配置里写了is-sharetrue也不会生效。Mixin 模式同样重写返回false其 Token 同样带随机串而 Statelessness 模式则完全不处理该配置。2. 使用 jwt-mixin 模式后is-concurrent必须为 trueis-concurrentfalse代表每次登录把旧登录顶下线。但 Mixin 模式的 Token 不记录在持久库中saveTokenToIdMapping是空操作框架拿不到旧 Token 的值技术上无法将其踢下线顶人/踢人 API 也随之不可用。因此 Mixin 模式下必须配置is-concurrenttrue允许同一账号并发在线。3. 使用 jwt-mixin 模式后max-try-times恒等于 -1max-try-times用于限制同一 Token 的尝试次数配合唯一性判断。Mixin 模式下 Token 内嵌随机串导致框架无法按原有逻辑判断 Token 唯一性为防止误判StpLogicJwtForMixin.getConfigOfMaxTryTimes 重写为恒返回-1不限次该配置在此模式下无效。十一、小结sa-token-jwt 插件通过重写StpLogic 静态SaJwtUtil/SaJwtTemplate工具的组合在保持 Sa-Token 全部 API 不变的前提下把登录态接入 JWT。落地时的检查清单引入sa-token-jwt依赖锁定 hutool-jwt 5.7.14 且避开 5.8.13/5.8.14配置sa-token.jwt-secret-key不要照抄示例字符串按业务需求三选一注入StpLogicJwtForSimple/StpLogicJwtForMixin/StpLogicJwtForStateless并接受其能力矩阵差异尤其 Mixin/Stateless 下踢人、顶人、会话搜索等 API 不可用多账号体系需手动为自定义StpXxxUtil调用setStpLogic需要更换签名算法时重写SaJwtTemplate并通过SaJwtUtil.setSaJwtTemplate全局替换记住三条配置约束Simple 下is-share无效、Mixin 下is-concurrent必须为 true、Mixin 下max-try-times恒为 -1。以上所有行为的依据均可在 sa-token-plugin/sa-token-jwt 模块源码与 sa-token-demo-jwt 示例工程 中逐行验证。【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考