实现 SSO 的关键不是让每个系统各管登录,而是抽出独立的认证中心(IdP),由它颁发可验证的令牌,各子系统(SP)只负责校验令牌、不再存密码。基于 OIDC + OAuth2.0 的授权码流程,用户登录一次后,访问任何接入的系统都凭同一令牌免登进入。下文给出角色划分、标准流程与可直接落地的令牌签发代码。
一、SSO 的三个核心角色
SSO 的本质是”在多个独立系统间共享认证状态”。无论用哪种协议,架构都围绕中央认证与跨系统会话共享展开。
| 角色 | 职责 | 示例 |
|---|---|---|
| IdP 认证中心 | 校验身份、签发令牌、管理全局会话 | Keycloak、Auth0、自建 |
| SP 业务系统 | 校验令牌、创建本地会话,不存密码 | OA、订单、支付 |
| User 用户 | 在 IdP 登录一次,访问全部 SP | 企业员工、终端用户 |
对于现代 Web 与 API,OAuth2.0 + OIDC 是首选:OAuth2.0 管授权,OIDC 在之上补身份层,开箱即得标准化登录。微服务架构里把 IdP、API 网关、各微服务分成三层,网关做统一令牌校验,下游服务再做精细授权。
二、标准登录流程(授权码模式)
用户首次访问 SP1,未登录即被重定向到 IdP;IdP 校验凭证后签发全局令牌并写全局会话 Cookie;SP1 校验令牌后建本地会话。之后再访问 SP2,IdP 发现全局会话有效,直接发针对 SP2 的令牌,用户免登进入。
- 用户访问 SP,SP 检测未登录,302 重定向到 IdP 的
/authorize; - 用户在 IdP 输入凭证,IdP 校验通过,生成授权码
code并重定向回 SP 的回调地址; - SP 后端用
code向 IdP 的/token换取 Access Token 与 ID Token(JWT); - SP 验证 ID Token 签名与
aud/iss/exp,提取用户身份,建立本地会话; - 用户访问其他 SP 时重复 1–4,因全局会话已存在,不再要求输入密码。
公共客户端(SPA、移动端)应使用授权码 + PKCE,避免授权码被拦截;服务端到服务端调用用 RFC 8693 的令牌交换或每次短生命令牌,维持最小权限。
2.1 令牌签发示例(Python)
用 PyJWT 签发和验证 ID Token:
import jwt, time, uuid
SECRET = "your-rsa-or-hmac-secret" # 生产用非对称密钥,私钥签名
def issue_id_token(user_id: str, role: str) -> str:
now = int(time.time)
payload = {
"iss": "https://idp.example.com", # 签发方
"sub": user_id, # 用户标识
"aud": "order-service", # 受众 SP
"role": role,
"iat": now,
"exp": now + 3600, # 1 小时过期
"jti": str(uuid.uuid4), # 防重放唯一 ID
}
return jwt.encode(payload, SECRET, algorithm="HS256")
def verify_id_token(token: str, audience: str) -> dict:
return jwt.decode(token, SECRET, algorithms=["HS256"], audience=audience)
Access Token 设为分钟级短生命周期,配刷新令牌轮换;前端用 httpOnly Cookie 存刷新令牌,避免长期凭据落浏览器。
三、跨域与跨系统如何打通
同主域(如 oa.company.com、crm.company.com)可用共享 Cookie/Session;前后端分离或跨域场景必须用令牌方案。JWT 经请求头 Authorization: Bearer 携带,不受 Cookie 域名限制。
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 同主域内部系统 | Cookie/Session 共享 | 配置简单 |
| 跨域 / 微服务 | JWT(无状态) | 易扩展、跨域友好 |
| 企业级 / 第三方对接 | OIDC + OAuth2.0 | 标准化、生态强 |
四、登出要同步
单点登出要求用户在任一 SP 退出时,IdP 销毁全局会话并通知其余 SP 销毁本地会话。OIDC 提供前端频道(iframe 重定向)与后端频道(webhook)两种登出传播;令牌用不透明令牌 + introspection 端点可即时撤销,JWT 则需维护短期黑名单。各 SP 的本地会话也要随全局登出一起清,否则会出现”退出一个系统另一个还在线”的割裂体验。
常见问题(FAQ)
Q1:SSO 和 OAuth2.0 是一回事吗?
不是。SSO 是目标,OAuth2.0 是手段,通常配 OIDC 补身份层。
Q2:JWT 怎么强制登出?
短过期 + 刷新令牌轮换,或用黑名单 / introspection 撤销。
Q3:多个 SP 必须同域名吗?
不必。用 Bearer 令牌跨域,IdP 统一签发即可。