
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),僅供參考