
Sa-Token SSO 多客户端差异化签名秘钥secret-key 配置与 getSignTemplate 重写全解析【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token在 Sa-Token SSO 单点登录体系中Client 端发起的 ticket 校验、单点注销等 HTTP 调用都依赖 API 签名timestamp nonce sign来防止请求被伪造。默认情况下所有应用共用一个全局签名秘钥sa-token.sign.secret-key当系统中接入多个 SSO Client 时若想让各应用持有独立秘钥、彼此不能互相“冒充”就需要为不同 Client 配置不同的秘钥并在 Server 端重写getSignTemplate函数实现按 client 标识路由秘钥。本文基于官方文档 sso-diff-key.md 与仓库源码完整讲解这套模式的配置步骤、底层签名校验机制、以及/sso/getData接口的一个高频踩坑点。一、为什么需要为不同 Client 配置不同秘钥SSO Client 端与 SSO Server 端之间的每次 HTTP 交互校验 ticket、单点注销、消息推送等都需要携带签名参数。签名校验所用的秘钥通过如下全局参数配置yaml 风格sa-token: sign: # API 接口调用秘钥 secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKorproperties 风格# 接口调用秘钥 sa-token.sign.secret-keykQwIOrYvnXmSDkwEiFngrKidMcdrgKor如果 SSO Client 端和 SSO Server 端配置的秘钥不同请求将无法调通Server 端会返回“无效签名”错误{ code: 500, msg: 无效签名9f1b453817bfeac56d2f772a66c01eb2, data: null }在多应用场景下全局单一秘钥意味着任何一个 Client 的秘钥泄露攻击者都能用它伪造请求访问 Server 端接口甚至冒充其他 client 的身份。因此 Sa-Token 支持“一个 Client 一套秘钥”的差异化模式让各应用之间无法互相冒充。二、签名校验机制秘钥到底在哪里参与运算理解差异化秘钥之前先看签名是如何生成和校验的。核心实现位于 SaSignTemplate.javacheckRequest(SaRequest request, String... paramNames)约 L374-L380是 Web 请求校验入口不指定参数名时校验请求全部参数指定时只校验指定参数内部通过checkParamMap依次校验三个必传参数timestamp时间戳、nonce防重放随机数、sign签名值任一缺失或非法都会抛出SaSignExceptionsign的合法性由isValidSign判定——将参与签名的参数与当前SaSignConfig中的secret-key一起计算签名后比对不一致即抛出无效签名xxxx异常对应错误码 CODE_12202。也就是说秘钥是签名算法的输入之一。Client 端用秘钥 A 生成 signServer 端若用秘钥 B 去验算结果必然不一致这就是“无效签名”报错的根本原因。而 SSO 模块在发起和接收这类请求时究竟“用哪个秘钥”就由 Server 端的getSignTemplate(client)决定——这正是差异化模式要重写的函数。三、第一步SSO Client 端配置 client 标识与各自秘钥在 Client 端需要新增一个参数sa-token.sso-client.client用于标识当前应用。该字段对应配置类 SaSsoClientConfig.java 中的client属性L41-L43源码注释明确说明“当前 Client 标识非必填不填时代表当前应用是一个匿名应用”。以 client1 为例完整配置如下。yaml 风格sa-token: sso-client: # 当前 client 标识 client: sso-client1 # ... sign: # sso-client1 使用的秘钥 secret-key: secret-key-xxxx-1properties 风格# 当前 client 标识 sa-token.sso-client.clientsso-client1 # sso-client1 使用的秘钥 sa-token.sign.secret-keysecret-key-xxxx-1client2 的配置同理sa-token: sso-client: # 当前 client 标识 client: sso-client2 # ... sign: # sso-client2 使用的秘钥 secret-key: secret-key-xxxx-2properties 风格# 当前 client 标识 sa-token.sso-client.clientsso-client2 # sso-client2 使用的秘钥 sa-token.sign.secret-keysecret-key-xxxx-2从 SaSsoClientTemplate.java 的源码结构看Client 端在向 Server 端发起 HTTP 调用如校验 ticket、单点注销、/sso/getData拉取数据时会把getClient()返回的标识放入请求参数/请求头中paramName.client其值在 ParamName.java L44 定义常量client。这保证了 Server 端能够识别“这次请求来自哪个 client”从而选用对应的秘钥验签。四、第二步SSO Server 端重写 getSignTemplate 函数Server 端要按 client 标识返回不同的签名模板。做法是新建CustomSaSsoServerTemplate.java继承SaSsoServerTemplate重写其getSignTemplate函数/** * 自定义 SaSsoServerTemplate 子类 */ Component public class CustomSaSsoServerTemplate extends SaSsoServerTemplate { // 存储所有 client 的秘钥 static MapString, SaSignTemplate signMap new HashMap(); static { signMap.put(sso-client1, new SaSignTemplate(new SaSignConfig(secret-key-xxxx-1))); signMap.put(sso-client2, new SaSignTemplate(new SaSignConfig(secret-key-xxxx-2))); signMap.put(sso-client3, new SaSignTemplate(new SaSignConfig(secret-key-xxxx-3))); // ... } Override public SaSignTemplate getSignTemplate(String client) { // 先从自定义的 signMap 中获取 SaSignTemplate saSignTemplate signMap.get(client); if (saSignTemplate ! null) { return saSignTemplate; } // 找不到就返回全局默认的 SaSignTemplate return SaManager.getSaSignTemplate(); } }几点实现要点每个秘钥对应一个独立的SaSignTemplate实例。SaSignTemplate构造时绑定一个SaSignConfig其中secret-key是验签的唯一秘钥来源因此“一个 client 一个 SaSignTemplate”就是差异化秘钥的落点兜底逻辑不可省略当请求携带的 client 未在 signMap 中注册时回退到SaManager.getSaSignTemplate()全局默认模板保证匿名或未注册 client 的行为与原版一致秘钥必须与 Client 端配置一一对应signMap 中sso-client1 - secret-key-xxxx-1必须与 client1 的sa-token.sign.secret-key完全一致否则依旧报“无效签名”。源码视角默认的 getSignTemplate 秘钥优先级不重写时框架内置的秘钥选择逻辑位于 SaSsoServerTemplate.java 的getSignTemplate(String client)方法L821-L836。其取值优先级为client 单独配置 (SaSsoClientModel.secretKey) SSO Server 模块全局配置 (sa-token.sso-server.secret-key) sign 模块默认配置 (sa-token.sign.secret-key)源码中还有一段针对匿名 client 的兜底getAnonClient()会在匿名应用未配置秘钥时直接取全局SaSignManager.getSaSignTemplate()的配置秘钥L332-L342。对比可以发现两条差异化路线路线做法适用场景配置驱动在 Server 端clients列表中为每个 client 配置secret-key字段client 数量少、秘钥可放入配置文件代码驱动重写getSignTemplate用自定义 signMap 管理秘钥client 数量多、秘钥需从配置中心/数据库动态获取getSignTemplate是 Server 端所有出向签名调用单点注销回调notifyClientLogout、消息推送pushMessage等的统一入口重写它即可一次性覆盖 ticket 校验、单点注销、消息推送等全部链路的秘钥路由。五、其它注意点/sso/getData 接口的签名校验坑有同学反馈集成“不同 SSO Client 配置不同秘钥”模式后客户端调用/sso/getData接口时会报如下错误无效签名5a7fc42836deba12d96527d43c1301ea或者参与参数签名的秘钥不可为空这大概率是因为在 sso-server 端自定义的/sso/getData接口在校验签名时忘了把 client 参数传给getSignTemplate导致 Server 端无法路由到正确的秘钥或者拿到了空秘钥的默认模板。修改方式如下// 示例获取数据接口用于在模式三下为 client 端开放拉取数据的接口 RequestMapping(/sso/getData) public SaResult getData(String apiType, String loginId) { System.out.println(---------------- 获取数据 ----------------); System.out.println(apiType apiType); System.out.println(loginId loginId); // ↓↓↓ ⚠️ 重点代码 ↓↓↓ // 校验签名只有拥有正确秘钥发起的请求才能通过校验 String client SaHolder.getRequest().getHeader(client); SaSsoServerProcessor.instance.ssoServerTemplate.getSignTemplate(client).checkRequest(SaHolder.getRequest()); // ↑↑↑ ⚠️ 重点代码 ↑↑↑ // 自定义返回结果模拟 return SaResult.ok() .set(id, loginId) .set(name, LinXiaoYu) .set(sex, 女) .set(age, 18); }关键在于两行的顺序与传参先从请求头取出client标识再用getSignTemplate(client)拿到该 client 专属的签名模板最后才执行checkRequest验签。凡是自定义了 SSO 相关 HTTP 接口不只是getData并需要验签的场景都应遵循这一模式。此外需要提醒is-check-sign参数SaSsoClientConfig 与 SaSsoServerConfig 中均有定义默认值为true源码注释明确其为“方便本地调试用的一个配置项生产环境请务必为 true”。调试阶段若暂时关闭签名校验上线前务必恢复否则差异化秘钥体系将形同虚设。六、相关配置项与源码索引配置项端说明源码定义位置sa-token.sign.secret-key全局签名模块默认秘钥所有未单独配置秘钥的调用回退到这里sign 模块SaSignConfigsa-token.sso-client.clientClient当前应用标识发起请求时自动附带SaSsoClientConfig.javasa-token.sso-client.secret-keyClientClient 端本地 API 调用签名秘钥可选同上sa-token.sso-server.secret-keyServerSSO 模块全局秘钥含匿名 client 默认值SaSsoServerConfig.javaclients列表中每个 client 的secret-keyServerclient 单独配置的秘钥优先级最高SaSsoClientInfo.javais-check-sign双端是否校验参数签名生产环境必须为 true两端配置类实现层面涉及的核心类SaSsoServerTemplate.javaServer 端模板类getSignTemplate(String client)为秘钥路由入口L821-L836SaSsoClientTemplate.javaClient 端模板类负责在请求中附带 client 标识并发起签名调用SaSignTemplate.java签名工具类checkRequest/isValidParamMap实现 timestamp、nonce、sign 三重校验。七、小结为不同 SSO Client 配置不同秘钥的完整落地路径是Client 端为每个应用配置sa-token.sso-client.client标识 各自独立的sa-token.sign.secret-keyServer 端继承SaSsoServerTemplate重写getSignTemplate(String client)用 client 标识到秘钥模板的映射完成路由未注册 client 回退全局默认模板自定义接口验签时如/sso/getData务必先取出请求中的client参数再传入getSignTemplate(client)否则会落入“无效签名”或“参与参数签名的秘钥不可为空”的坑。这套模式与框架内置的“client 单独配置 SSO 模块全局 sign 模块默认”三级秘钥优先级相配合使多应用 SSO 体系在共享一套 Server 的同时仍能实现按应用隔离的接口调用安全。【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考