Sa-Token 插件开发实战:四种扩展方式与 SPI 注册机制完整解析
Sa-Token 插件开发实战四种扩展方式与 SPI 注册机制完整解析【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token本文为 Sa-Token 插件开发指南的系统化解读以“策略重写、组件替换、上下文实现、自由封装”四种插件开发方式为主线结合仓库中SaStrategy、SaManager、SaTokenPluginHolder的真实源码讲清每个扩展点的底层机制与插件注册SpringBoot SPI / 通用 SPI的完整落地路径。读完你可以独立为 Sa-Token 编写“引入即生效”的插件 jar 包。一、插件是什么可拔插的扩展点插件Plugin从字面意思理解就是可拔插的组件其作用是在不改变 Sa-Token 现有架构的情况下替换或扩展一部分底层代码逻辑。从源码结构看Sa-Token 的扩展能力建立在两个核心设施之上这也是所有插件开发方式的技术基础SaManager全局组件管理器框架的所有可替换组件都以setXxx/getXxx形式定义在其上且 getter 内部做了“未注入则使用默认实现”的懒加载。查看 SaManager 源码可替换的全局组件包括组件作用默认实现替换入口SaTokenDao持久化组件Token/Session 存储SaTokenDaoDefaultImpl内存SaManager.setSaTokenDao(...)StpInterface权限数据源StpInterfaceDefaultImplSaManager.setStpInterface(...)SaTokenContextWeb 框架上下文SaTokenContextForThreadLocalSaManager.setSaTokenContext(...)SaTempTemplate临时 Token 认证模块SaTempTemplateSaManager.setSaTempTemplate(...)SaJsonTemplateJSON 转换器SaJsonTemplateDefaultImplSaManager.setSaJsonTemplate(...)SaHttpTemplateHTTP 请求模板SaHttpTemplateDefaultImplSaManager.setSaHttpTemplate(...)SaSerializerTemplate序列化器SaSerializerTemplateForJsonSaManager.setSaSerializerTemplate(...)SaSameTemplateSame-Token 同源系统认证SaSameTemplateSaManager.setSaSameTemplate(...)SaLog日志输出器SaLogForConsoleSaManager.setLog(...)SaTotpTemplateTOTP 动态密码SaTotpTemplateSaManager.setSaTotpTemplate(...)以 JSON 转换器为例SaManager#setSaJsonTemplate 的 getter 逻辑是如果saJsonTemplate null才兜底创建SaJsonTemplateDefaultImpl。这意味着只要你在启动时注入过自定义实现框架就永远不会再加载默认实现——这就是“组件替换”生效的底层原理。SaStrategy全局策略对象框架把一些“关键逻辑算法”抽象成可重写的函数式字段见 SaStrategy。它不是组件对象而是算法级别的扩展点两者互补。二、插件开发的四种方式为 Sa-Token 开发插件有四种可行方式自定义全局策略更改全局组件实现实现自定义SaTokenContext其它自由扩展。方式 1自定义全局策略Sa-Token 将框架的一些关键逻辑抽象出一个统一的概念 ——策略并统一定义在SaStrategy中。SaStrategy的每一个函数都可以单独重写以“自定义 Token 生成策略”这一需求为例// 重写 Token 生成策略 SaStrategy.instance.createToken (loginId, loginType) - { return SaFoxUtil.getRandomString(60); // 随机60位长度字符串 };就像变量的重新赋值一样只需重新指定一个新的策略函数即可自定义 Token 生成的逻辑。对照 SaStrategy 源码可以看到默认的createToken策略会根据配置项tokenStyle生成不同风格的 Token支持的取值有uuid、simple-uuid、random-32、random-64、random-128、tiktik 风格即2_14_16分段随机串。重写策略后登录时StpLogic生成 Token 的过程将完全走你的函数。SaStrategy中全部可重写策略一览均为public函数式字段可直接赋值也可用同名setXxx连缀风格方法策略字段功能说明createToken创建 Token 的策略createSession创建 Session 的策略返回SaSession实例sessionClassType反序列化SaSession时默认指定的类型hasElement判断集合中是否包含指定元素先精确匹配、再模糊匹配generateUniqueToken生成唯一式 token 的算法循环生成 唯一性校验超maxTryTimes次抛异常autoRenew是否自动续期active-timeoutcreateStpLogic创建StpLogic的算法多账号体系定制点routeMatcher路由匹配策略默认抛NotImplException需上下文集成包提供实现createSaRequest/createSaResponse/createSaStorage创建上下文包装对象的策略同上默认未实现corsHandleCORS 策略处理函数默认空实现getSaTokenConfig获取SaTokenConfig的策略默认null表示走框架内置逻辑先读SaManager.config为空时自动读取sa-token.properties。注意源码注释特别提示策略内部不要反向调用SaManager.getConfig()否则会陷入无限递归一个细节routeMatcher、createSaRequest等策略默认直接抛NotImplException从源码结构看它们需要由具体的 Web 框架集成包如sa-token-spring-boot-starter在注入阶段提供实现——这解释了为什么引入框架 Starter 后路由拦截等功能才“自动可用”。方式 2更改全局组件实现你可以找到不符合你需求的组件重新定义一个子类。以“临时令牌认证”模块为例自定义SaTempTemplate源码的子类/** * 临时认证模块 自定义子类实现 */ Component public class MySaTempTemplate extends SaTempTemplate { Override public String createToken(Object value, long timeout, boolean isRecordIndex) { System.out.println(------- 自定义一些逻辑 createToken ); return super.createToken(value, timeout, isRecordIndex); } Override public Object parseToken(String token) { System.out.println(------- 自定义一些逻辑 parseToken ); return super.parseToken(token); } }这种方式适合“在原有行为上追加逻辑”的场景子类可以只重写需要干预的方法其余方法继续super.xxx(...)委托给父类风险最小。方式 3实现自定义 SaTokenContextSaTokenContext是对接不同 Web 框架的上下文接口。核心是四个对象SaTokenContext上下文管理器、SaRequest请求对象、SaResponse响应对象、SaStorage一次请求范围内的上下文数据读写。其动机在于调用StpUtil.isLogin()时并没有传递HttpServletRequest参数框架是通过全局过滤器把请求对象包成SaRequest/SaResponse/SaStorage存入ThreadLocal实现的不同框架的请求对象形态不同WebFlux 中只有ServerHttpRequest因此必须封装一层统一接口来屏蔽差异。完整的实现步骤含包装类编写、上下文过滤器注册请参阅仓库内的 自定义 SaTokenContext 指南。方式 4其它自由扩展这种方式无需注入全局组件替换内部实现你可以在 Sa-Token 的基础之上封装任何代码进行功能扩展——例如基于StpUtil、SaSession封装业务级登录态管理、基于事件中心SaTokenEventCenter实现自定义监控等。判断边界是只要不侵入sa-token-core源码、以独立 jar 的形式叠加能力就属于插件的合理形态。三、插件注册让插件代码真正注入项目完成插件开发之后还需要解决“如何让插件代码注入到项目中”。分两种情况情况 1只打算自己的项目使用这个插件情况 2准备提交 PR 到 Sa-Token 仓库让更多人使用。情况 1只在自己的项目中使用SpringBoot 项目在自定义插件类上添加Component注解即可Component public class MySaTempTemplate extends SaTempTemplate { // ... }项目启动时sa-token-spring-boot-starter 集成包会扫描到该自定义组件。从源码看这一过程由 SaBeanInject 完成其构造函数与一组Autowired(required false)的 setter 方法会在 Spring 容器启动时把扫描到的SaTokenDao、StpInterface、SaTokenContext等 Bean 逐一写入SaManager对应槽位。重写全局策略的代码同样可以借助PostConstruct做到启动时自动执行PostConstruct public void rewriteSaStrategy() { // 重写 token 生成策略 SaStrategy.instance.createToken (loginId, loginType) - { return SaFoxUtil.getRandomString(60); }; }非 SpringBoot 项目环境无法自动注入保底方案是在main方法中手动注册组件public static void main(String[] args) { // 示例手动替换 Sa-Token 内部组件 // Sa-Token 大部分全局组件都定义在 SaManager 之上 SaManager.setSaTempTemplate(new MySaTempTemplate()); // 示例手动重写 Sa-Token 全局策略 SaStrategy.instance.createToken (loginId, loginType) - { return SaFoxUtil.getRandomString(60); }; }情况 2作为可复用插件发布这种情况复杂一些因为你很难在插件内部写代码去帮助“插件使用者”完成注册。官方给出的解决方案是两条路线路线 A把注册交给使用者。插件开发者只负责开发自定义组件注册过程完全交给使用者——sa-token-jwt、sa-token-thymeleaf等官方插件都是这样做的。这并非妥协反而给插件使用者更大的自由度可自选生效时机与配置。路线 B利用 SPI 机制实现“引入即注册”。再按目标环境细分SpringBoot 专用插件利用 SpringBoot 自身的 SPI 机制SpringBoot 2 格式创建resources/META-INF/spring.factories文件内容为org.springframework.boot.autoconfigure.EnableAutoConfiguration插件完全限定名SpringBoot 3 格式创建resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件内容直接写插件完全限定名这样别人引入该插件时Spring 会按 SPI 文件指定的地址加载插件类做到插件引入即注册。通用型插件Solon、JFinal 等任意环境SpringBoot 的 SPI 文件在其它框架中无法被识别好在 Sa-Token 提供了自己的 SPI 机制所有环境均可使用。需要两步第 1 步新建SaTokenPluginForXxx类实现SaTokenPlugin接口推荐定义在cn.dev33.satoken.plugin包下/** * SaToken 插件安装插件作用描述 */ public class SaTokenPluginForXxx implements SaTokenPlugin { Override public void install() { // 书写需要在项目启动时执行的代码例如 // SaManager.setXxx(new SaXxxForXxx()); } }SaTokenPlugin 接口只有两个方法install()安装插件必须实现与destroy()卸载插件默认空实现。第 2 步新建resources/META-INF/satoken/cn.dev33.satoken.plugin.SaTokenPlugin文件填写插件类的完全限定名cn.dev33.satoken.plugin.SaTokenPluginForXxx底层加载流程源码级印证插件管理器 SaTokenPluginHolder 负责所有插件的加载与卸载_loaderPluginsBySpi 通过ClassLoader.getResources(META-INF/satoken/cn.dev33.satoken.plugin.SaTokenPlugin)扫描所有 jar 包下该路径的 SPI 文件注意是getResources复数天然支持多个插件 jar 共存忽略空行与#注释行反射实例化每个实现类loaderPlugins 依次对每个插件调用installPlugin日志中会打印SPI plugin loading start ... / end ...installPlugin 有严格校验插件不可为空、同一插件不可重复安装抛出SaTokenPluginException并依次执行“install 前置钩子 → 插件 install() → install 后置钩子”框架还预留了完整钩子体系onInstall接管默认 install 行为、onBeforeInstall/onAfterInstall/onDestroy/onBeforeDestroy/onAfterDestroy。注意onAfterInstall的语义——如果插件已经安装完毕注册时钩子函数会立即执行见 onAfterInstall这保证了插件使用者无论何时接入钩子都能正确回调触发时机在各框架 Starter 的组件注入类中统一调用pluginHolder.init()例如 Spring Boot 集成包 SaBeanInject 在构造函数末尾执行pluginHolder.init()init内部有isLoader幂等保护多次调用只加载一次Solon、Loveqq 等其它框架的注入类同样做了这一调用。插件生命周期测试可参考 SaTokenPluginHolderTest。官方真实案例sa-token-fastjson插件正是“通用型 SPI 插件”的完整示范。查看 SaTokenPluginForFastjsonpublic class SaTokenPluginForFastjson implements SaTokenPlugin { Override public void install() { // 设置JSON转换器Fastjson 版 SaManager.setSaJsonTemplate(new SaJsonTemplateForFastjson()); // 重写 SaSession 生成策略 SaStrategy.instance.createSession SaSessionForFastjsonCustomized::new; // 指定 SaSession 类型 SaStrategy.instance.sessionClassType SaSessionForFastjsonCustomized.class; } }三个动作恰好覆盖了前文两种扩展方式组件替换setSaJsonTemplate 策略重写createSession、sessionClassType配合其 JSON 模板实现 SaJsonTemplateForFastjson 与定制会话类 SaSessionForFastjsonCustomized实现了“引入依赖即全局切换 Fastjson 序列化”的效果。四、练练手开发一个 sa-token-hutool-json 插件给你一个实战题目开发一个sa-token-hutool-json插件要求引入该插件后自动替换掉 Sa-Token 的 JSON 序列化方案为 hutool-json 模块。没有思路时对照sa-token-fastjson的源码结构即可完成步骤如下在插件模块中实现SaJsonTemplateForHutoolJson implements SaJsonTemplate在toJsonString/parseJson/writeValue/readValue等方法中调用 hutool-json 的 API对应 fastjson 版 SaJsonTemplateForFastjson 的每个方法签名新建SaTokenPluginForHutoolJson implements SaTokenPlugininstall()中执行SaManager.setSaJsonTemplate(new SaJsonTemplateForHutoolJson())如需定制 Session 序列化类型再重写createSession与sessionClassType两个策略在resources/META-INF/satoken/cn.dev33.satoken.plugin.SaTokenPlugin中登记插件类全限定名将插件作为依赖引入任一 Sa-Token 项目启动后观察日志中的SPI plugin loading start ...与 JSON 输出是否为 hutool 风格即可验证注册生效。五、小结扩展点分两层SaManager管理“组件”可换实现类SaStrategy管理“策略”可换算法二者覆盖绝大多数定制需求注册分两类自用项目用Component/PostConstruct/main方法手动注册可复用插件用 SpringBoot SPI 文件或 Sa-Token 自有 SPIMETA-INF/satoken/目录实现引入即生效设计取向官方优先推荐“插件只开发、注册交使用者”SPI 自动注册是可选增强两者在sa-token-jwt、sa-token-thymeleaf、sa-token-fastjson等插件中都有现成参照。更多插件生态alone-redis、jwt、sso、oauth2 等模块可继续浏览 插件文档目录 所在目录下的各专题指南。【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考