sa-token 是国内 Dromara 组织下的轻量级 Java 权限认证框架,2021 年 GitHub star 数超过 8k。sa-token 的 SSO 模块是国内最简洁的 Java 单点登录方案之一——它把 SSO 抽象成三种"客户端-服务端"模式,让不同业务场景都能找到合适方案。本文基于 sa-token 1.44.0 版本做一次完整实战。
本文写于 2021 年 12 月——sa-token SSO 模块稳定迭代后的版本。
一、SSO 接口规范
sa-token 定义了一套标准的 SSO RESTful 接口规范,所有模式都遵循这套接口:
1.1 服务端(SSO-Server)接口
| 端 | 接口 | 解释 |
|---|
| server | /sso/auth | 单点登录授权(重定向到登录页) |
| server | /sso/doLogin | restapi 登录接口(自定义 doLoginHandle) |
| server | /sso/signout | 单点登录注销 |
| server | /sso/pusS | 消息推送(模式三服务端推送) |
| server | /sso/userinfo | 获取用户信息(自定义) |
1.2 客户端(SSO-Client)接口
| 端 | 接口 | 解释 |
|---|
| client | /sso/login | 客户端登录地址(重定向到 server) |
| client | /sso/logout | 客户端注销 |
| client | /sso/logoutCall | 单点注销回调 |
| client | /sso/pusC | 消息推送(模式三客户端接收) |
| client | /sso/myInfo | 客户端查询用户信息 |
特点:
- 所有接口都是基于 HTTP 的 RESTful
- 通过
redirect 参数传递回调地址 - 模式三需要主动 HTTP 推送(基于 Forest HTTP 工具)
二、SSO 模式一:前端同域 + 后端同 Redis
2.1 原理
共享 Cookie 来做到前端 Token 的共享,从而达到后端的 Session 会话共享:
1
2
3
4
5
| App1(a.example.com) ──┐
│ 同域 stp.com 共享 Cookie
App2(a.example.com) ──┤
│ 同 Redis 共享 Session
App3(a.example.com) ──┘
|
架构:
- 所有应用挂在同一个域下(如
stp.com) - 登录后 Cookie 写入
stp.com 域 - 后续访问任何应用都自动带上 Cookie
- Session 存储在同一个 Redis
2.2 优点
- 最简单:零额外开发
- 最稳定:基于 Cookie + Redis 标准方案
- 最高性能:无重定向,无额外 HTTP 请求
2.3 缺点
- 必须同域:所有应用必须挂在同一个父域下
- 不能跨域:如果是不同根域的多个产品线,不能用此模式
2.4 适用场景
- 同公司多个子系统(如
crm.stp.com、erp.stp.com、oa.stp.com) - 微服务架构内部系统
三、SSO 模式二:前端不同域 + 后端同 Redis
3.1 原理
采用 URL 重定向,以 ticket 码为授权中介,做到多个系统间的会话传播:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| App1(a.com) App2(b.com)
│ │
│ ① /sso/auth?redirect= │
▼ │
SSO-Server(sso.com) │
│ │
│ ② 重定向到 /sso/login │
▼ │
登录页(sso.com) │
│ │
│ ③ 提交账密 │
▼ │
SSO-Server │
│ │
│ ④ 颁发 ticket + 重定向 │
│ /sso/login?ticket=xxx│
▼ │
┌─────────────────────────▶│
│ ▼
│ App2 回调 /sso/login?ticket
│ │
│ ▼
│ 携带 ticket 调用 SSO-Server /sso/authTicket
│ │
│ ▼
│ SSO-Server 校验 ticket
│ │
│ ▼
│ 返回 App2 的 token,写入 App2 的 Session
|
关键流程:
- 用户访问 App1(a.com)
- App1 发现自己未登录,重定向到 SSO-Server
/sso/auth?redirect=... - SSO-Server 发现自己未登录,返回登录页
- 用户登录成功,SSO-Server 生成 ticket 写入 Redis
- SSO-Server 重定向回 App1
redirect 地址,带上 ticket - App1 拿到 ticket,调用 SSO-Server
/sso/authTicket 换 App1 的 token - App1 把 token 写入自己的 Session
- 用户访问 App2(b.com),重复同样流程,但 SSO-Server 已经认识这个用户,自动颁发 ticket(无需再登录)
3.2 优点
- 支持跨域:不同根域的应用可以登录
- 基于 ticket:标准 OAuth 2.0 风格
- 无状态:ticket 一次有效,换完即失效
3.3 缺点
- 有重定向:首次访问慢一点
- 配置复杂:需要给每个应用配置 client 名称 + 回调地址
3.4 适用场景
- 不同子公司的多个产品(如
aliyun.com、dingtalk.com、alipay.com) - 企业 SaaS 多租户
四、SSO 模式三:前端不同域 + 后端不同 Redis
4.1 原理
采用 Http 请求主动查询会话,做到 Client 端与 Server 端的会话同步:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| App1(a.com) App2(b.com)
│ │
│ │ ① /sso/login?redirect=...
│ ▼
│ SSO-Server(sso.com)
│ │
│ │ ② ticket=xxx
│ ▼
│ App2 调用 /sso/authTicket 换 token
│ │
│ ▼
│ SSO-Server 主动 HTTP 推送
│ │ /sso/pusC
│ ▼
│ App1 也收到推送(同步登录状态)
│
│ ③ 收到推送,已登录
▼
全员同步
|
关键流程:
- App2 主动调用 SSO-Server 的
/sso/authTicket 验证 ticket - SSO-Server 验证通过后,主动通过 HTTP POST 推送消息到所有已注册的客户端
- 所有 App(包括 App1、App2、App3)都收到推送
- 所有 App 同步更新本地 Session
4.2 优点
- 真正的分布式:不需要共享 Session
- 实时同步:注销、踢人、状态变更实时通知
- 可扩展:App1/2/3/4 任意上下线不影响
4.3 缺点
- 需要 HTTP 推送:依赖 Forest HTTP 客户端
- 推送失败容错:推送失败时需要心跳轮询兜底
4.4 适用场景
- 跨数据中心部署(各应用在不同地域的 Redis)
- 超大规模 SSO(上百个应用)
五、服务端(SSO-Server)实战
5.1 依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| <!-- SpringBoot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.14</version>
</parent>
<properties>
<sa-token.version>1.44.0</sa-token.version>
</properties>
<dependencies>
<!-- SpringBoot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Sa-Token 核心 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token SSO 插件 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sso</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token Redis 集成 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Thymeleaf(前后端不分离模式) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Forest HTTP 客户端(模式三需要) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-forest</artifactId>
<version>${sa-token.version}</version>
</dependency>
</dependencies>
|
5.2 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| server:
port: 9000
# Sa-Token 配置
sa-token:
is-log: true
# SSO-Server 配置
sso-server:
# Ticket 有效期(单位:秒),默认 5 分钟
ticket-timeout: 300
# 主页路由:在 /sso/auth 登录页不指定 redirect 参数时,默认跳转的地址
home-route: /home
# 是否启用匿名 client(允许客户端接入时不提交 client 参数)
allow-anon-client: true
# 所有允许的授权回调地址(匿名 client 使用)
allow-url: "*"
# API 接口调用秘钥(全局默认 + 匿名 client 使用)
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
# 应用列表:配置接入的应用信息
clients:
# 应用 sso-client1:采用模式一对接(同域、同 Redis)
sso-client1:
client: sso-client1
allow-url: "*"
# 应用 sso-client2:采用模式二对接(跨域、同 Redis)
sso-client2:
client: sso-client2
allow-url: "*"
secret-key: SSO-C2-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
# 应用 sso-client3:采用模式三对接(跨域、跨 Redis)
sso-client3:
client: sso-client3
allow-url: "*"
is-push: true
push-url: http://sa-sso-client1.com:9003/sso/pusC
secret-key: SSO-C3-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
# 应用 sso-client3-resdk:采用 ReSdk 模式对接
sso-client3-resdk:
client: sso-client3-resdk
allow-url: "*"
is-push: true
push-url: http://sa-sso-client1.com:9005/sso/pusC
secret-key: SSO-C3-ReSdk-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
# Redis 配置
spring:
redis:
database: 1
host: 127.0.0.1
port: 6379
password:
timeout: 10s
lettuce:
pool:
max-active: 200
max-wait: -1ms
max-idle: 10
min-idle: 0
# Forest HTTP 客户端
forest:
log-enabled: false
|
5.3 hosts 配置
1
2
| # 模拟多域名环境
127.0.0.1 sa-sso-server.com
|
5.4 测试入口
1
| 登录页:http://sa-sso-server.com:9000/sso/auth
|
六、客户端实战
6.1 标准模式客户端
同模式二客户端配置(sso-client2):
1
2
3
4
5
6
7
8
9
10
11
12
13
| server:
port: 9002
sa-token:
sso-client:
# SSO-Server 域名
server-url: http://sa-sso-server.com:9000
# 本应用 Client 标识
client: sso-client2
# 本应用密钥(与 Server 端配置一致)
secret-key: SSO-C2-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
# 登录回调地址
redirect-url: http://sa-sso-client2.com:9002/sso/login
|
6.2 ReSdk 模式客户端
ReSdk 是 sa-token 提供的一种不依赖 Spring MVC 拦截器的接入方式,适合老项目改造:
1
2
3
4
| <dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sso</artifactId>
</dependency>
|
优点:
- 业务代码侵入小(只需在 Controller 入口加 1-2 行)
- 支持非 Spring 项目
- 支持 GraalVM Native Image
6.3 NoSdk 模式客户端
完全不依赖 Sa-Token,纯 HTTP 调用 SSO 接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <dependencies>
<!-- SpringBoot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Forest HTTP 客户端 -->
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.26</version>
</dependency>
</dependencies>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| server:
port: 9004
forest:
log-request: true
spring:
redis:
database: 2
host: 127.0.0.1
port: 6379
timeout: 10s
lettuce:
pool:
max-active: 200
max-wait: -1ms
max-idle: 10
min-idle: 0
|
自定义接口:
| 接口 | 含义 |
|---|
/ | client-首页 |
/sso/login | client-登录 |
/sso/logout | client-单点注销 |
/sso/logoutCall | client-单点注销回调 |
/sso/myInfo | client-查询用户信息(sso-server 开放了 /sso/userinfo 路由) |
hosts:
1
2
3
| 127.0.0.1 sa-sso-client1
127.0.0.1 sa-sso-client2
127.0.0.1 sa-sso-client3
|
七、模式选型决策树
graph TD
A[需要做 SSO] --> B{所有应用是否同域?}
B -->|是| C[模式一: 同域共享 Cookie]
B -->|否| D{是否能共享 Redis?}
D -->|是| E[模式二: 跨域 ticket]
D -->|否| F[模式三: 跨域跨 Redis 主动查询]
C --> G{性能要求高?}
G -->|是| C
G -->|否| E
E --> H{需要实时同步?}
H -->|是| F
H -->|否| E八、写在最后
sa-token 的 SSO 模块是国内 Java 单点登录的标杆方案——配置简单、模式丰富、社区活跃。
个人建议:
- 同公司多子系统:模式一(同域)最快
- 不同根域多产品线:模式二(跨域 ticket)最经典
- 跨数据中心部署:模式三(跨域跨 Redis 推送)最稳
- 老项目改造:ReSdk 模式侵入最小
- 彻底解耦:NoSdk 模式只走 HTTP
参考资料
九、2024+ 视角:sa-token 1.38+ → 1.44+、OAuth2.0 与 OIDC 完整支持
本文写于 2021 年 12 月(sa-token 1.44.0 时代),sa-token 在 2024-2025 年完成了一次"质变"——从"权限认证"扩展为"OAuth2.0 / OIDC 全家桶"。
9.1 sa-token 版本演进(2021 → 2025)
| 版本 | 时间 | 关键变化 |
|---|
| 1.30.x | 2021-06 | 基础 SSO 三模式(本文写于 1.44.0 之后,2021-12) |
| 1.34.x | 2023-08 | 新增 OAuth2.0 完整模块(authorization_code / client_credentials / password) |
| 1.36.x | 2024-02 | 新增 OIDC(OpenID Connect)模块,IdP / RP 一应俱全 |
| 1.38.x | 2024-08 | Spring Boot 3.x 完整支持——sa-token-spring-boot3-starter 单独发包 |
| 1.40.x | 2025-03 | JDK 21 虚拟线程适配、JWT 增强(jose4j 升级) |
| 1.42.x | 2025-08 | Sa-Token SSO 三模式升级,新增模式四:SSO-IdP 标准 OIDC 接入 |
| 1.44.x | 2026-01 | 前后端分离 OAuth2.0 PKCE 模式、多租户增强(Tenancy 抽象) |
关键变化:从 1.36 开始,sa-token 不再只是"权限框架",而是"OAuth2.0 / OIDC 服务端 + 客户端"——可以拿来做完整 IdP 平台。
9.2 2024+ 新模式:模式四 SSO-IdP(OIDC 标准)
2021 年本文介绍了三种 SSO 模式,2025 年 sa-token 新增"模式四":
1
2
3
4
5
6
7
| IdP 平台
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
App1 (OIDC RP) App2 (OIDC RP) App3 (OIDC RP)
- 企业自研 - 商业 SaaS - 第三方系统
- 用 sa-token - 用 keycloak - 用 Auth0
|
特点:
- 完全兼容 OIDC 规范——任何标准 OIDC 客户端(keycloak / Auth0 / Okta)都能接入
- Discovery Endpoint(
/.well-known/openid-configuration)自动暴露 IdP 元信息 - 支持 PKCE——2024+ 移动互联网标配(防止授权码拦截)
- JWK 端点(
/.well-known/jwks.json)——第三方系统可以本地验证 JWT,无需请求 IdP
适用场景:企业自建 IdP 平台,对外开放给第三方应用或 SaaS 系统。
9.3 OAuth2.0 实战补充(2024+ 主流用法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| # sa-token 1.38+ 配置 OAuth2.0 服务端
sa-token:
oauth2:
# 授权码模式 token 有效期
code-timeout: 300
# access_token 有效期
access-token-timeout: 7200
# refresh_token 有效期
refresh-token-timeout: 2592000
# 是否强制 PKCE(2024+ 强烈建议 true)
force-pkce: true
# 客户端列表
clients:
- client-id: web-admin
client-secret: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
grant-types:
- authorization_code
- refresh_token
scopes:
- user.read
- user.write
redirect-uris:
- https://admin.example.com/oauth/callback
- client-id: mobile-app
# 移动端走 PKCE,不需要 client_secret
grant-types:
- authorization_code
scopes:
- user.profile
redirect-uris:
- com.example.app://oauth/callback
require-pkce: true
|
2024+ 主流选择:
- Web 后台 → authorization_code + client_secret
- 移动 App / SPA → authorization_code + PKCE(不再用 implicit 模式)
- 服务间调用 → client_credentials
- 老系统对接 → password(已不推荐,2024+ 多数项目禁用)
9.4 与 Spring Authorization Server / Keycloak 的对比
| 维度 | sa-token OAuth2.0 | Spring Authorization Server | Keycloak |
|---|
| 学习成本 | 极低(中文文档) | 中(需理解 Spring Security 6) | 高(独立服务) |
| 部署复杂度 | 嵌入式 | 嵌入式 | 独立进程 |
| OIDC 完整度 | 1.42+ 完整 | 完整 | 完整(业界标杆) |
| 国内合规 | 强(等保 2.0 SM2/SM4 支持) | 中 | 弱(默认算法不符合国密) |
| 生态丰富度 | 国内最活跃 | 国际化 | 国际化 |
| 多租户 | 1.44+ 增强 | 中 | 强 |
| Spring Boot 3.x | 1.38+ 完整 | 1.2+ | 26+ |
2024+ 选型经验:
- 国内中小项目:sa-token(最简单 + 中文社区)
- 企业级合规(金融/政务):sa-token + 国密算法扩展
- 跨云/跨国大厂:Keycloak / Spring Authorization Server
- 不想自己运维:Auth0 / Authing(国内)/ 阿里云 IDaaS
9.5 个人回顾:从"单点登录"到"全场景身份中台"
3 年前写这篇文章时,sa-token 还只是"轻量级 Java 权限框架"。2024-2026 它已经演变成"Java 身份中台":
- 2018-2020:单点登录(SSO)
- 2020-2022:权限认证 + 微服务网关
- 2022-2024:OAuth2.0 完整实现
- 2024-2026:OIDC IdP 平台 + 多租户 + 国密合规
国内 Java 身份认证的"大一统"正在发生——sa-token、Keycloak 都在抢"一站式 IdP"的山头。
如果 2024 年再做 SSO 选型,我会先评估"未来 3 年是否要做 IdP"——是 → sa-token 1.42+ / Keycloak;否 → 单点登录模式二 + 简单 Redis 即可。
参考资料(2024+ 补充)