单点登录 (SSO)
SSO 把认证能力集中到可信中心,业务系统不碰密码,只认中心签发的身份结果。认证集中、协议标准、会话分层、授权解耦。
#type / synthesis
#status / evergreen
#tech / security
#tech / dev / backend
[!info] related notes
- 所属 MOC: security-moc, backend-dev-moc
- 协议层: oauth2, oidc, saml
- 相关概念: cookie-session-token, jwt
单点登录 (SSO)
一句话核心
SSO 不是”用户只登一次”,而是把认证能力集中到一个可信中心,业务系统自己不碰密码,只认这个中心签发的身份结果。
三层架构
| 层 | 职责 |
|---|---|
| 身份认证层 | 验证用户名密码、短信、企业微信、AD、MFA |
| 身份断言层 | 验证后如何安全告诉各系统”这个人是谁” |
| 业务会话层 | 各子系统如何建立自己的登录态 |
SSO 解决什么问题
- 认证统一:用户不用在 A/B/C 系统分别输密码
- 身份信任传递:A 系统相信认证中心说”这个用户已登录”
不等于权限统一。 SSO 只证明”你是谁”(Authentication),“你能看什么”是授权系统的事(Authorization)。
参与方角色
| 角色 | 别名 | 职责 |
|---|---|---|
| 用户端 | — | 浏览器、App、小程序 |
| 认证中心 | IdP / Auth Server / SSO Server | 登录认证、签发票据、维护全局登录态、MFA、对接 LDAP/AD/企微 |
| 业务系统 | SP / Client / Relying Party | 发现未登录→跳转认证中心→校验结果→建立本地会话 |
| 用户目录 | — | 数据库 / LDAP / AD / 企业微信 |
协议选型
| 协议 | 适合 | 特点 |
|---|---|---|
| oidc | 现代 Web/App/API | 基于 OAuth2,id_token/access_token/refresh_token,开发体验好 |
| saml | 老企业系统、政企、SaaS 联邦 | XML 断言,浏览器重定向/POST,企业集成多但开发体验不如 OIDC |
| CAS | 传统内部门户、高校 | 中央票据模型清晰,现代生态不如 OIDC |
结论:新系统优先 OIDC,老企业联邦可能需 SAML,长期向 OIDC 收敛。 不要自己发明协议。
标准登录流程(OIDC 授权码模式)
用户 → 业务系统A: 访问资源
业务系统A → 用户: 跳转到 SSO(带 state/nonce/redirect_uri)
用户 → SSO: 带上 SSO Cookie
SSO → 用户: 未登录→展示登录页 / 已登录→直接签发 code
用户 → 业务系统A: 带 code 回调
业务系统A → SSO: 用 code 换 token(后端调用)
SSO → 业务系统A: 返回 id_token/access_token/refresh_token
业务系统A: 校验 id_token(签名/iss/aud/exp/nonce)
业务系统A → 用户: 建立本地 session cookie
第二次访问业务系统 B 时: B 跳去 SSO → SSO 发现全局会话还在 → 直接签发 code → B 建立本地 session → 用户无感登录。
两层会话
| 会话 | 存储位置 | 证明什么 |
|---|---|---|
| 全局 Session | sso.example.com 域下 | 用户在统一认证中心已登录 |
| 本地 Session | app1.example.com 各自域下 | 这个系统自己认可该用户已登录 |
关键设计对象
用户身份标识
user_id(sub):内部唯一主键,不变username:登录名,可改display_name:展示名,可改
不要拿手机号、邮箱当永远不变主键。
Token 三类
| Token | 用途 | 建议 |
|---|---|---|
id_token | 身份信息(你是谁) | 短效,含 iss/sub/aud/exp/nonce |
access_token | 调用 API 鉴权 | 更短效,不塞太多业务权限 |
refresh_token | 续签 | 可撤销、可轮换 |
用户体系
users表:用户主信息user_identities表:外部身份映射(password / ldap / wechat / dingding → 同一个 user_id)orgs/roles/user_roles:组织与权限
单点登出
| 方式 | 效果 |
|---|---|
| 本地登出 | 只删业务系统 session,不动 SSO 全局 session |
| 全局登出 | 销毁 SSO 全局 session + 通知各业务系统清理本地 session |
通知方式:前端跳转式 / 后端 back-channel 回调式 / 消息总线事件驱动。
安全必做
- HTTPS 强制
state防 CSRF,nonce防重放redirect_uri精确白名单匹配- PKCE 用于 public client(SPA/原生 App)
- 登录限频、设备指纹风控、图形验证码
- 登录成功后轮换 session id
- JWT 签名校验 + key rotation
- 敏感信息不明文塞 token
- 不要将
access_token存 localStorage(用 HttpOnly cookie)
常见坑
| 坑 | 后果 |
|---|---|
| 业务系统自己也存密码 | 破坏统一认证 |
| redirect_uri 校验不严格 | 重定向劫持 |
| access_token 存 localStorage | XSS 拿走 |
| JWT 一发就不管 | 无法撤销/登出失效 |
| 没有统一登出链路 | 用户以为退出了,其他系统还在线 |
| 所有权限塞 token | 权限一变旧 token 还在飞 |
| 依赖 iframe 第三方 cookie | 现代浏览器越来越不可靠 |
推荐落地方案
- 协议:首选 OIDC,兼容老系统补 SAML
- 登录方式:用户名密码 + 企微/钉钉扫码 + MFA + LDAP/AD
- 会话:SSO 全局 session 存 Redis,业务系统各自维护本地 session,关联
sso_session_id - Token:id_token 短效,access_token 更短效,refresh_token 可撤销轮换
- 登出:支持 local + global logout,推荐 back-channel logout
- 安全:HTTPS + PKCE + state/nonce + 精确 redirect_uri + 风控限流 + 审计日志 + 密钥轮换
架构判断 7 问
- 用户身份唯一主键是什么?
- 全局 session 和本地 session 分别在哪?
- 用的是什么标准协议?
- redirect_uri / state / nonce / PKCE 是否正确实现?
- 单点登出怎么做?
- 权限归 SSO 还是业务系统?边界清不清晰?
- 密钥、审计、风控、限流有没有闭环?