
Sa-Token SSO 二次开发完全指南Server 端与 Client 端工具类、可重写策略源码级解析【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token本篇指南完整梳理 Sa-Token SSO 模块二次开发所需掌握的全部 API 面SaSsoServerUtil与SaSsoClientUtil两大工具类的每个方法、Server 端与 Client 端全部可重写策略的默认实现与自定义写法并深入到SaSsoServerTemplate、SaSsoClientTemplate、策略类与配置类的源码细节帮助开发者在理解底层调用链的基础上独立完成 SSO 认证中心与服务端的定制化开发、消息推送、单点注销、Ticket 下发校验等场景的二次扩展。一、SSO 二次开发的整体结构Util 门面 → Template 模板 → Strategy 策略在动手阅读各方法前先建立整体认知。Sa-Token SSO 模块位于仓库 sa-token-plugin/sa-token-sso在cn.dev33.satoken.sso包下采用三层结构Util 层门面SaSsoServerUtil、SaSsoClientUtil对外提供静态方法内部委托给 TemplateTemplate 层核心实现SaSsoServerTemplate、SaSsoClientTemplate承载 Ticket 增删改查、重定向 URL 构建校验、消息推送、单点注销等全部业务逻辑Strategy 层可重写策略SaSsoServerStrategy、SaSsoClientStrategy以函数式接口暴露可替换的扩展点。以 SaSsoServerUtil.java 为例每个静态方法都只是对SaSsoServerProcessor.instance.ssoServerTemplate的同名方法做转发public static SaSsoServerTemplate getSsoTemplate() { return SaSsoServerProcessor.instance.ssoServerTemplate; }Processor如 SaSsoServerProcessor.java是 Template 的持有者而 Template 内部又持有一个strategy字段。因此二次开发时你面对的是两条可扩展路径直接调用 Util 方法完成业务编排或者覆写 Strategy 策略改变默认行为。下文先逐一讲解 Util 全部方法再分别展开两端可重写策略。二、Sso-Server 工具类SaSsoServerUtil 全方法详解SaSsoServerUtil是 SSO 认证中心Server 端二次开发的核心门面所有方法均可通过SaSsoServerUtil.xxx()直接调用。按其职责可分为五大类。2.1 Ticket 操作增删改、查询、校验与索引Ticket 是 SSO 登录凭证Server 端签发、Client 端换取会话。相关方法如下// 增删改 // 删除 Ticket SaSsoServerUtil.deleteTicket(String ticket); // 根据参数创建一个 ticket 码并保存client客户端标识loginId账号 idtokenValue会话 Token // 返回生成的 ticket 码 SaSsoServerUtil.createTicketAndSave(String client, Object loginId, String tokenValue); // 查 // 查询 ticket 对应信息如果 ticket 无效则返回 null SaSsoServerUtil.getTicket(String ticket); // 查询 ticket 指向的 loginId如果 ticket 码无效则返回 null SaSsoServerUtil.getLoginId(String ticket); // 查询 ticket 指向的 loginId并转换为指定类型 SaSsoServerUtil.getLoginId(String ticket, ClassT cs); // 校验 // 校验 Ticket无效 ticket 会抛出异常 SaSsoServerUtil.checkTicket(String ticket); // 校验 Ticket 码无效 ticket 会抛出异常如果此 ticket 是有效的则立即删除 SaSsoServerUtil.checkTicketParamAndDelete(String ticket); // 校验 Ticket无效 ticket 会抛出异常如果此 ticket 是有效的则立即删除额外校验 client 归属 SaSsoServerUtil.checkTicketParamAndDelete(String ticket, String client); // ticket 索引 // 查询 指定 client、loginId 其所属的 ticket 值 SaSsoServerUtil.getTicketValue(String client, Object loginId);底层实现位于 SaSsoServerTemplate.java几个值得注意的实现细节Ticket 的存储与时效saveTicket将TicketModel对象写入SaManager.getSaTokenDao()有效期取自getServerConfig().getTicketTimeout()默认60 * 5秒见 SaSsoServerConfig.java存储 key 形如{tokenName}:ticket:{ticket}Ticket 码的生成randomTicket通过SaFoxUtil.getRandomString(64)生成 64 位随机串createTicketAndSave在保存 Ticket 的同时还会调用saveTicketIndex保存client loginId → ticket 的反查索引key 形如{tokenName}:ticket-index:{client}:{loginId}这正是getTicketValue能由账号反查 ticket的原因一次性 TicketcheckTicketParamAndDelete在成功校验后立即deleteTicket并同步删除索引保证 Ticket 只可被消费一次传入client参数时还会比对ticketModel.getClient()与入参是否一致不一致抛出SaSsoException错误码CODE_30011这就是该 ticket 不属于 clientxxx异常的来源无参校验的细节checkTicketParamAndDelete(ticket)实际委托给checkTicketParamAndDelete(ticket, SaSsoConsts.CLIENT_WILDCARD)通配符表示跳过 client 归属校验。2.2 Client 信息获取Server 端需要维护一份 Client接入应用清单相关方法// 获取所有 Client SaSsoServerUtil.getClients(); // 获取应用信息无效 client 返回 null SaSsoServerUtil.getClient(String client); // 获取应用信息无效 client 则抛出异常 SaSsoServerUtil.getClientNotNull(String client); // 获取匿名 client 信息 SaSsoServerUtil.getAnonClient(); // 获取所有需要接收消息推送的 Client即 isPush 为 true 的 Client SaSsoServerUtil.getNeedPushClients();从源码看SaSsoServerTemplate.java#L274-L358getClients()返回getServerConfig().getClients()的 value 集合即配置在sa-token.sso-server.clients下的SaSsoClientModel列表getClientNotNull在 client 为空时若开启了allowAnonClient则返回匿名 client 配置否则抛异常CODE_30013未能获取应用信息getAnonClient从 Server 全局配置中组装一个匿名 client 模型其secretKey优先级为Server 全局secretKey→ sign 模块默认配置getNeedPushClients只返回getIsPush() true的 client这是pushToAllClient广播的目标集合。2.3 重定向 URL 构建与校验SSO 流程中 Server 端负责把携带 Ticket 的重定向地址回给 Client安全校验集中在以下两个方法// 构建 URLsso-server 端向 sso-client 下放 ticket 的地址 SaSsoServerUtil.buildRedirectUrl(String client, String redirect, Object loginId, String tokenValue); // 校验重定向 url 合法性非法时抛出 SaSsoException SaSsoServerUtil.checkRedirectUrl(String client, String url);buildRedirectUrl的实现链路SaSsoServerTemplate.java#L372-L385为先checkRedirectUrl校验地址合法性 → 删除旧 Ticket →createTicketAndSave签发新 Ticket → 将 ticket 参数拼接到 redirect 地址上返回。checkRedirectUrl的校验顺序SaSsoServerTemplate.java#L427-L474值得单独强调这是 SSO 防 Ticket 劫持的关键防线必须是合法 URL截取掉?后的 query 部分禁止 URL 中出现、%40、%2540——防止攻击者构造http://sa-sso-client1.com:9003sa-token.com这类地址绕过 allow-url 白名单把 Ticket 下发到第三方服务器截取出的地址必须命中该 client 配置的allowUrl白名单否则抛CODE_30002非法redirect。同时配置校验方法checkAllowUrlListStaticMethodSaSsoServerTemplate.java#L488-L539对allow-url配置本身也有两条硬性约束*通配符只允许出现在最后一位否则可能被http://sa-token.com/a.sa-sso-client1.com绕过*出现在末尾时其前一位必须是/或:即应写为http://domain/*或http://domain:*禁止写http://domain*。配置项一旦不合法会在setAllowUrl时提前抛出CODE_30015异常让开发者尽早发现错误。2.4 单点注销// 指定账号单点注销 SaSsoServerUtil.ssoLogout(Object loginId); // 指定账号单点注销可自定义注销参数与忽略某个 client SaSsoServerUtil.ssoLogout(Object loginId, SaLogoutParameter logoutParameter, String ignoreClient);ssoLogout的完整注销流程SaSsoServerTemplate.java#L624-L643分三步执行消息推送阶段pushToAllClientByLogoutCall向所有开启isSlo且需要推送的 client 异步推送单点注销回调消息会话清理阶段遍历该账号SaSession上挂载的 client 注册信息列表SSO_CLIENT_MODEL_LIST_KEY_逐个异步notifyClientLogout通知对应 client 下线本地注销Server 端自身执行getStpLogicOrGlobal().logout(loginId, logoutParameter)。此外registerSloCallbackUrlSaSsoServerTemplate.java#L551-L583是模式三is-httptrue下 Client 登录时注册注销回调的底层方法它把client sloCallbackUrl记录进账号的 Session且当注册数量超过maxRegClient默认 32-1 表示无限时按先进先出策略自动清退最早登录的 client。2.5 消息推送// 向指定 Client 推送消息入参为 clientModel 或 client 标识均可 SaSsoServerUtil.pushMessage(SaSsoClientModel clientModel, SaSsoMessage message); SaSsoServerUtil.pushMessage(String client, SaSsoMessage message); // 向指定 client 推送消息并将返回值转为 SaResult SaSsoServerUtil.pushMessageAsSaResult(SaSsoClientModel clientModel, SaSsoMessage message); SaSsoServerUtil.pushMessageAsSaResult(String client, SaSsoMessage message); // 向所有 Client 推送消息 SaSsoServerUtil.pushToAllClient(SaSsoMessage message); // 向所有 Client 推送消息并忽略掉某个 clientignoreClient 填 null 代表不忽略 SaSsoServerUtil.pushToAllClient(SaSsoMessage message, String ignoreClient);消息推送的实现要点SaSsoServerTemplate.java#L697-L764pushMessage会先调用message.checkType()校验消息类型msgType不可为空否则抛CODE_30022再通过getSignTemplate(client).addSignParamsAndJoin(message)为消息追加 API 签名参数最后通过strategy.sendRequest发起 HTTP 请求pushToAllClient内部遍历getNeedPushClients()并用strategy.asyncRun对每个 client异步并行推送默认实现是new Thread(...).start()消息载体SaSsoMessage本身是一个LinkedHashMap的扩展见 SaSsoMessage.java支持set(key, value)链式传参其校验 ticket单点注销等内置消息类型常量定义在 SaSsoConsts.java。三、Sso-Client 工具类SaSsoClientUtil 全方法详解SaSsoClientUtil是接入方Client 端二次开发的门面除文档列出的方法外还包含getData数据查询系列。3.1 构建交互地址// 构建 URLServer 端 单点登录授权地址 // 形如http://sso-server.com/sso/auth?redirecthttp://sso-client.com/sso/login?backhttp://sso-client.com SaSsoClientUtil.buildServerAuthUrl(String clientLoginUrl, String back);buildServerAuthUrl的实现SaSsoClientTemplate.java#L125-L155做了三件事拼接serverUrl authUrl默认/sso/auth见 SaSsoClientConfig.java并附上client标识对back参数做 URL 编码后拼接到clientLoginUrl已带?back或back时自动跳过避免重复 back 参数最终以redirect参数形式拼到 Server 授权地址上。使用注意如果clientLoginUrl传入的是已编码状态则框架无法识别其中的 back 参数调用方必须在编码前自行拼接 back 参数。3.2 单点注销// 指定账号单点注销loginId 为本地账号 id框架会按策略转换为认证中心 id SaSsoClientUtil.ssoLogout(Object loginId); // 指定账号单点注销 SaSsoClientUtil.ssoLogout(Object loginId, SaLogoutParameter logoutParameter);Client 端注销与 Server 端的区别在于它先调用strategy.convertLoginIdToCenterId将本地 loginId 转换为认证中心 id再buildSignoutMessage构造注销消息并pushMessageAsSaResult推送给认证中心由认证中心统一通知各端下线SaSsoClientTemplate.java#L252-L264。若 Server 响应的状态码非 200则抛出CODE_30006异常作为兜底推送成功后本地仍会执行一次logout防止极端场景下中心未通知到本端。3.3 消息推送// 向 sso-server 推送消息 SaSsoClientUtil.pushMessage(SaSsoMessage message); // 向 sso-server 推送消息并将返回值转为 SaResult SaSsoClientUtil.pushMessageAsSaResult(SaSsoMessage message); // 构建消息校验 ticketticketticket 码ssoLogoutCallUrl单点注销时的回调 URL SaSsoClientUtil.buildCheckTicketMessage(String ticket, String ssoLogoutCallUrl); // 构建消息单点注销 SaSsoClientUtil.buildSignoutMessage(Object loginId, SaLogoutParameter logoutParameter);消息构建与推送的实现对应 SaSsoClientTemplate.java#L175-L235buildCheckTicketMessage生成msgType MESSAGE_CHECK_TICKET的消息携带client、ticket、ssoLogoutCall三个参数buildSignoutMessage生成msgType MESSAGE_SIGNOUT的消息携带client、loginId、deviceIdpushMessage将消息拼接到 Server 的 push-url默认/sso/pushS并追加签名后发起请求。消息的消费由 Server 端注册的SaSsoMessageCheckTicketHandle、SaSsoMessageSignoutHandle处理器完成见 SaSsoServerTemplate.java#L55-L58。四、Sso-Server 端全部可重写策略策略对象通过SaSsoServerProcessor.instance.ssoServerTemplate.strategy获取类型为SaSsoServerStrategy定义见 SaSsoServerStrategy.java。共 6 个可重写扩展点SaSsoServerProcessor.instance.ssoServerTemplate.strategy.sendRequest url - { // 发送 Http 请求的处理函数默认SaManager.getSaHttpTemplate().get(url) // 二次开发可在此处统一替换 HttpClient 实现、增加日志、超时控制等 return ...; }; SaSsoServerProcessor.instance.ssoServerTemplate.strategy.asyncRun fun - { // 使用异步模式执行一个任务默认new Thread(() - fun.run()).start() // 二次开发可替换为线程池提交如 ExecutorService.submit(fun::run) }; SaSsoServerProcessor.instance.ssoServerTemplate.strategy.notLoginView () - { // 未登录时返回的 View默认返回提示文案字符串 // 二次开发可在此返回自定义的登录页面视图 return ...; }; SaSsoServerProcessor.instance.ssoServerTemplate.strategy.doLoginHandle (name, pwd) - { // SSO-Server 端登录函数默认直接返回 SaResult.error()即拒绝所有登录 // 二次开发必须覆写此函数校验账号密码并返回 SaResult如 // if(sa.equals(name) 123456.equals(pwd)) { return SaResult.ok(登录成功); } // return SaResult.error(账号或密码错误); return ...; }; SaSsoServerProcessor.instance.ssoServerTemplate.strategy.jumpToRedirectUrlNotice (redirectUrl) - { // SSO-Server 端在授权重定向之前的通知默认空实现 // 可用于记录重定向日志、埋点统计等 }; SaSsoServerProcessor.instance.ssoServerTemplate.strategy.checkTicketAppendData (loginId, result) - { // SSO-Server 端在校验 ticket 后给 sso-client 端追加返回信息的函数默认原样返回 result // 典型场景在 result 中追加该账号在认证中心的扩展数据头像、昵称、权限列表等随校验结果一并返回给 Client return result; };其中doLoginHandle是 SSO-Server 端集成时几乎必改的策略——默认实现直接返回SaResult.error()意味着所有账号都无法登录。改写它的典型姿势为SaSsoServerProcessor.instance.ssoServerTemplate.strategy.doLoginHandle (name, pwd) - { if (sa.equals(name) 123456.equals(pwd)) { return SaResult.ok(登录成功); } return SaResult.error(账号或密码错误); };另外SaSsoServerStrategy还内置了一个便捷方法requestAsSaResult(String url)SaSsoServerStrategy.java#L87-L91它调用sendRequest发送请求再通过SaManager.getSaJsonTemplate().jsonToMap(body)将响应体解析为SaResultpushMessageAsSaResult系列方法正是基于它实现的。五、Sso-Client 端全部可重写策略Client 端策略对象为SaSsoClientProcessor.instance.ssoClientTemplate.strategy类型SaSsoClientStrategy见 SaSsoClientStrategy.java共 4 个可重写扩展点SaSsoClientProcessor.instance.ssoClientTemplate.strategy.sendRequest url - { // 发送 Http 请求的处理函数默认SaManager.getSaHttpTemplate().get(url) // 与 Server 端同名策略作用一致可替换为自定义 HTTP 客户端 return ...; }; SaSsoClientProcessor.instance.ssoClientTemplate.strategy.ticketResultHandle (ctr, back) - { // 自定义校验 ticket 返回值的处理逻辑 // 每次从认证中心获取校验 ticket 的结果后调用 // 参数loginId, back返回值返回给前端的值 // 默认值为 null不启用启用后可在 Client 端对校验结果做二次加工 return ...; }; SaSsoClientProcessor.instance.ssoClientTemplate.strategy.convertCenterIdToLoginId (centerId) - { // 转换认证中心 centerId 本地 loginId默认原样返回 centerId // 当本地账号 id 与认证中心账号 id 不一致时覆写此方法做映射 return ...; }; SaSsoClientProcessor.instance.ssoClientTemplate.strategy.convertLoginIdToCenterId (loginId) - { // 转换本地 loginId 认证中心 centerId默认原样返回 loginId // 与上一策略互为逆操作单点注销时会把本地 id 转回中心 id 再推送注销消息 return ...; };这一对convertCenterIdToLoginId/convertLoginIdToCenterId是 Client 端做账号 id 映射的关键扩展点例如认证中心使用全局唯一 uid而本地业务使用自增主键时可在此实现双向转换保证登录、注销流程中两套 id 体系正确对齐转换逻辑的调用点分别位于 SaSsoClientTemplate.java#L253 的注销链路以及校验 ticket 的处理链路中。六、二次开发实战组合 Util 与 Strategy 完成定制场景基于上文的方法清单与策略扩展点常见的三类定制场景可这样落地场景一登录时向 Client 端下发扩展用户数据。不修改任何调用代码只覆写checkTicketAppendData策略在校验 ticket 后向result追加数据Client 端通过ticketResultHandle接收即可。场景二将默认的线程异步改为线程池。覆写 Server 端asyncRun策略例如改为executor.submit(() - fun.run())即可统一管理消息推送与注销通知的异步线程资源避免默认new Thread在高并发下频繁创建线程。场景三账号体系隔离下的单点注销。Client 端覆写convertLoginIdToCenterId/convertCenterIdToLoginId完成 id 映射随后直接调用SaSsoClientUtil.ssoLogout(loginId)框架会自动完成本地 id → 中心 id → 推送认证中心 → 中心通知各端下线 → 本地兜底注销的完整链路。需要指出上述所有 Util 方法与策略均建立在 SSO 配置项之上Server 端的ticketTimeoutTicket 有效期默认 300 秒、allowUrl授权回调白名单、secretKey签名密钥、isSlo单点注销开关、maxRegClient单账号可注册 client 上限、allowAnonClient匿名 client 开关等定义在 SaSsoServerConfig.javaClient 端的serverUrl、client、authUrl、signoutUrl、pushUrl、getDataUrl、isHttp、secretKey、isCheckSign等定义在 SaSsoClientConfig.java。其中两端的isCheckSign默认均为true生产环境务必保持开启仅作为本地调试时可临时关闭。七、延伸阅读上述全部方法的源码实现Server 端 SaSsoServerTemplate.javaClient 端 SaSsoClientTemplate.javaSSO 消息模型与内置消息处理器SaSsoMessage.java 及message/handle包下的SaSsoMessageCheckTicketHandle、SaSsoMessageSignoutHandle、SaSsoMessageLogoutCallHandle单测佐证仓库 sa-token-plugin/sa-token-sso/src/test 目录下template包的SaSsoServerUtilTest、SaSsoClientUtilTest、SaSsoServerTemplateTest、SaSsoClientTemplateTest覆盖了 Ticket 增删改查、重定向 URL 校验、消息推送、单点注销等核心方法可作为二次开发时的行为参考SSO 模式说明与整体集成方式可继续阅读仓库文档 sso-type3.md、sso-server.md 与 sso-apidoc.md配合 sa-token-demo-sso3-client 等示例工程对照学习。【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考