何为单点登录(SSO)?
单点登录(SSO)这玩意儿,很多人第一次接触是在公司内网:你开了 OA,再点文档系统,不用再输密码;再点工单系统,还是不用。
这时候你会产生一种错觉:好像“全公司共用一个账号密码”就完事了。
其实不是。SSO 的精髓不是“共用密码”,而是“共用一次登录的结果”,也就是:我在一个可信的地方证明过“我是谁”,其他系统就别再折腾我了。
先一句话:什么是单点登录?
单点登录(Single Sign-On, SSO):用户在一个统一的认证中心登录一次之后,就能访问多个相互信任的系统/应用,而不需要在每个系统里重复登录。
它解决的主要是两件事:
- 用户少输几次账号密码(体验)
- 公司少养几套“用户体系 + 权限体系 + 登录逻辑”(治理)
先认清角色:谁负责“认人”,谁负责“用人”?
SSO 场景里最常见有两个角色:
- IdP(Identity Provider,身份提供方):负责认证,也就是“我来确认你是谁”。常见是统一登录中心。
- SP(Service Provider,服务提供方):具体业务系统,比如 OA、工单、文档、财务系统等。
你可以把它理解成:
- IdP = 门禁闸机
- SP = 各个办公室
办公室只关心“你有没有门禁权限”,不想天天自己发卡、自己验指纹。
一个最常见的 SSO 流程(浏览器跳转版)
以“你打开工单系统”举例,最典型的流程是“重定向来回跳”:
- 你访问工单系统(SP)
- SP 发现你没登录,于是把你 302 到登录中心(IdP)
- 你在 IdP 登录成功(可能是账号密码/短信/扫码/企业微信)
- IdP 给你一个“证明”(ticket / code / assertion / token 之类的东西),再把你 302 回 SP
- SP 拿着这个“证明”去 IdP 验一下真伪,验过后给你建本地会话(session/cookie),你就进去了
这套过程你肉眼看到的就是:地址栏闪了几下,最后回到了业务系统,登录状态还在。
流程图(把“跳来跳去”画出来)
sequenceDiagram
participant U as Browser
participant SP as SP(业务系统)
participant IdP as IdP(登录中心)
U->>SP: GET /app
SP-->>U: 302 Location: IdP /authorize?redirect_uri=SP/callback
U->>IdP: GET /authorize ...
IdP-->>U: 登录页面 / 或直接识别已有 IdP 会话
U->>IdP: 提交登录(账号/扫码/MFA)
IdP-->>U: 302 Location: SP /callback?code=xxx
U->>SP: GET /callback?code=xxx
SP->>IdP: POST /token(用 code 换 token/校验断言)
IdP-->>SP: id_token / access_token(或校验结果)
SP-->>U: Set-Cookie: sp_session=...
U->>SP: GET /app(带 sp_session)
SP-->>U: 200 OK
示例代码(JavaScript):SP 侧最常见的两段(拦截 + 回调)
下面用“接近业务代码但不绑定具体实现细节”的 JavaScript 示例写一下,核心就是两段:没登录就跳 IdP;回调里拿 code 去换 token,然后给自己发 cookie。
1) 业务接口/页面的登录拦截(Auth Guard)
1 | import crypto from 'node:crypto'; |
2) IdP 回调处理(用 code 换登录态)
1 | const SP_CLIENT_SECRET = process.env.SP_CLIENT_SECRET; |
那个“证明”到底是什么?
这就是 SSO 里最容易把人绕晕的地方:不同体系,名字不一样,但核心是一样的:
- 它能证明“用户已在 IdP 登录过”
- 它最好是一次性的/短期有效的
- SP 可以拿它去 IdP 校验,或者它本身可校验(带签名)
常见几种主流方案你大概听过:
1) SAML(企业内网很常见)
- IdP 发给 SP 一个 SAML Assertion(通常是 XML)
- Assertion 里包含用户信息、有效期、签名等
- SP 校验签名,确认“确实是 IdP 发的”
优点:企业生态成熟;缺点:XML + 各种配置,工程体验相对“硬核”。
2) OAuth 2.0 / OIDC(现代 Web 更常见)
这里容易混:
- OAuth 2.0:更偏“授权”(允许 A 系统代表你去访问 B 系统资源)
- OIDC(OpenID Connect):在 OAuth 2.0 基础上补了“身份认证”,更适合“登录”
常见落地是 Authorization Code Flow:
- SP 把你跳到 IdP,让你登录并授权
- IdP 回给 SP 一个
code - SP 用
code换id_token(身份)和access_token(资源访问)
“单点”到底单在哪?别误会了
SSO 的“单点”不是说:
- 所有系统只有一份 cookie
- 所有系统都共享同一个 session
更真实的情况是:
- IdP 有自己的登录态(一般是 IdP 域名下的 cookie/session)
- 每个 SP 也会建自己的登录态(SP 域名下的 cookie/session)
SSO 做的事情是:当某个 SP 没登录时,它可以“借助 IdP 的登录态”快速把自己也登录上。
所以你会看到一个很合理的现象:
- 你清了某个业务系统的 cookie,它会要求你登录
- 但它又会立刻把你跳到 IdP,然后瞬间把你跳回来(因为 IdP 还记得你)
这就是“单点登录”的真实运行方式。
你以为最难的是“登录”,其实是“登出”
登录能统一,登出就麻烦了:你到底要退出哪儿?
1) 单点登出(SLO)
理想情况:你在任何一个系统点退出,所有系统都跟着退出。
实现成本:偏高。因为你要让各个 SP 配合,挨个通知、挨个清会话,还要处理“有系统挂了/没响应”的情况。
2) 更常见的现实做法
很多公司做的是“半单点登出”:
- 退出当前系统(清这个 SP 的 cookie)
- 退出登录中心(清 IdP 的 cookie)
这样下次再访问其他系统,它们需要重新走一遍 SSO 流程才能登录上(因为 IdP 已经不认你了)。
前端最常见的坑(别问,问就是踩过)
1) Cookie SameSite 导致“怎么跳都登录不上”
现代浏览器对第三方 cookie 越来越严格,尤其是 IdP 和 SP 不同域名时,跳转链路里 cookie 是否能带上,会受 SameSite 影响。
你会遇到一种特别折磨的现象:你明明登录成功了,但回到业务系统又说你没登录,然后又跳回登录中心,如此循环。
这类问题通常要从:
- cookie 的
SameSite配置 - 是否 HTTPS(
Secure) - 是否是 iframe 场景(更麻烦)
这几个方向去查。
额外补一张:单点登出(SLO)大概怎么“通知全家”
登出这个话题比较“现实主义”:你想一键退出所有系统,就要把“通知链路”也搭起来。
flowchart LR
U[用户] --> SP1[SP:工单]
U --> SP2[SP:文档]
U --> SP3[SP:OA]
U --> IdP[IdP:登录中心]
SP1 -->|退出| IdP
IdP -->|通知登出| SP2
IdP -->|通知登出| SP3
IdP -->|清理 IdP 会话| IdP
SP2 -->|清理 SP 会话| SP2
SP3 -->|清理 SP 会话| SP3
2) 把 token 存 localStorage,然后被 XSS 一锅端
如果你用的是 OIDC/OAuth 的 token 体系,前端总有人想图省事:
- access_token 存 localStorage
然后你只要有一次 XSS,就相当于把门禁卡递给了攻击者。
更稳的方式通常是:
- 敏感 token 尽量别落到 JS 可读的存储里
- 或者把关键会话交给后端维护(BFF 模式),前端只拿 httpOnly cookie
3) 回调地址配置得像散装的
SSO 基本都依赖回调地址(redirect_uri / ACS URL)。配置一旦散装:
- 多环境(dev/staging/prod)经常漏配
- 前端换域名/换路径,登录就全炸
建议从第一天就把回调地址治理成“可配置、可管理、可审计”的资产,别让它变成“谁改谁背锅”的黑盒。
怎么判断你们到底需不需要 SSO?
如果符合这些情况,SSO 基本就值得上:
- 你们有多个系统,用户需要频繁切换
- 用户体系重复建设严重(每个系统都维护一份用户/登录/密码策略)
- 安全要求高(MFA、统一风控、统一审计、统一封禁)
反过来,如果就一个系统,或者系统之间不怎么互相访问,上 SSO 可能是“为了解决不存在的问题”。
SSO 这事儿说到底就一句话:把“认证”从业务系统里抽出来,交给专业的认证中心做。业务系统别天天纠结你是谁,它应该更关心:你能不能干这件事(授权/权限)。
Happy Coding!
