KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
SSO 应用课 · 课程导读 — keel 龙骨
SSO 工程课:协议、令牌与会话 的参考信息:SSO 应用课 · 课程导读
这门课不站在「怎么把 SSO 接进来」的起点上,而是站在接进来之后它不按预期工作的起点上。
认证这一层有个特点:它对的时候什么也不说。跳转成功、令牌拿到、用户进来——
一切顺利时你看不到任何东西。只有它坏了,你才会看到一堆参数名:code、state、nonce、session_state、iss、kid……
它们每一个都对应一条一次性的约束,而故障几乎总是「某条约束在步骤之间被破坏」。
七章的顺序是刻意排的:
先站到高处(01 选型)
→ 把一次登录逐步走通(02 授权码流 + PKCE)
→ 把它带回来的三样东西拆开(03 令牌与校验)
→ 回答「为什么第二次不用再登录」(04 会话与登出)
→ 把视角从一个应用抬到多个系统(05 联邦与多租户)
→ 用前面所有结论去制造故障(06 故障注入与排障)
→ 把教学替身逐条换成真实依赖(07 生产边界)
你现在的起点
先花一分钟对一下。下面五条你都能说清,这门课的前两章可以直接跳读:
- 知道 SSO 解决的是「同一个身份在多个系统间被认成同一个人」,并且已经亲手跑起过一个 IdP;
- 接进去过一次应用,走过「跳去登录 → 登录 → 跳回来」,但说不清回跳地址上每个参数的作用;
- 见过
invalid_grant/redirect_uri这类报错,但排障靠试,没有固定的查看顺序; - 知道
OAuth 2.0、OIDC、SAML这三个词都存在,但说不清它们分别在哪一层; - 系统里已经有别人的数据了,知道「认证」与「授权」是两件事。
其中任何一条答不上来,都属于本课的读者面。
主案例:一个 IdP、两个应用、两个用户
全课用一个案例反复演进,不换例子:
| 角色 | 标识 | 说明 |
|---|---|---|
| IdP | realm sso-lab,issuer http://localhost:8081/realms/sso-lab |
唯一签发身份结论的一侧 |
| 应用 A | sp-spa,公开客户端 + PKCE,回跳 http://localhost:8090/callback |
拿不到凭证的客户端,只能靠 PKCE |
| 应用 B | sp-portal,机密客户端 + client_secret,回跳 http://localhost:8091/callback |
有服务端的一侧,能保守秘密 |
| 用户 | alice |
两个应用都要认成同一个人 |
| 用户 | bob |
用来验证「不是所有登录都等价」 |
两个客户端刻意选了两种类型:04 章的单点登录判据、06 章的一整类故障
(client_secret 放在哪里、能不能放进前端)都靠这个对比才说得清楚。
course/project/ 里这套配置是同一份,代码化之后可以改、可以断、可以注入。
章节地图
| 章节 | 关键问题 | 产物落点 |
|---|---|---|
| 01 全景与选型 | 为什么会有「登录」这件事变得复杂?三条协议各在哪一层?我这个场景选哪个? | 一张分层对照表 + 一个选型判断顺序 |
| 02 授权码流与 PKCE | 第一步 GET 为什么给的是登录页?回跳的四个参数各是什么?PKCE 防住了谁? | 一次完整登录的逐步报文(含失败路径) |
| 03 令牌与校验 | 三个 token 各管什么?JWT 三段与 kid?每一项校验漏了会出什么事? |
一张「claim → 防什么 → 漏了会怎样」对照表 |
| 04 会话与登出 | 单点登录靠什么成立?「已登录」记在哪一侧?登出为什么要清两处? | 单点登录/单点登出的判据 + 前后 cookie 对照 |
| 05 联邦与多租户 | 一个应用接多家 IdP 怎么区分?sub 能当账号吗?企业接入要交换什么? |
一份企业接入清单 + 账号关联与离职回收的路径 |
| 06 故障注入与排障 | 断了怎么定位?五类典型故障各自长什么样? | 一张「症状 → 去哪看 → 根因」表 |
| 07 生产边界 | 单实例够用吗?会话存哪?密钥怎么轮换?代理后面 issuer 为什么变了? |
一张「教学替身 → 真实替换点」表 |
你会反复用到的三种「看」
- 看报文:不猜实现,直接看请求 URL、响应状态码、回跳参数、令牌里的 claim。
协议层的一切都能用「谁发给谁、带了什么、状态码多少」说清。 - 看载体:某个状态被记在了哪一侧——浏览器 cookie、IdP 会话、应用自己的 session、
令牌本身。单点登录与单点登出的全部难点都在这里。 - 看一次性能不能用:
code用一次、nonce绑一次、at_hash绑一次、
refresh token 可以重复用(默认不做 reuse detection)——这几条不对称的规则正是故障来源。
三个必须先纠偏的认知
code不是 token,它换不出任何东西。 它只是一张经过浏览器、必须再带上code_verifier才能兑换的凭据。把它当成「已经登录」是排障时第一个要排掉的假设。- 令牌验过签名 ≠ 这次登录合法。 签名只说明「这段内容确实是 IdP 签的」,
还要逐个比对iss/aud/exp/nonce/at_hash——少一项就是一个可被利用的口子。 - 单点登录不是「IdP 记得你」,是「浏览器还带着那个 cookie」。 换个浏览器、
换个设备、清了 cookie,同一个用户就要重新登录一次。这条决定了它在哪些场景里做不到你要的效果。
配套项目与本课的关系
course/project/ 是一个只用标准库的最小 OIDC 实现(一个最小 OP + 一个最小 RP + 一个会管 cookie 的浏览器)。
正文每一处「报文里到底交换了什么」都能在它里面找到对应的代码行,每条失败路径都有对应的测试断言。
学习顺序建议:先读正文建立判断,再跑 examples/01 看完整流程,
最后自己改一处代码去制造 06 章里的故障。
读完之后
- 想补「起一个 IdP、把应用接进去」的操作面,去《SSO 基础课》;
- 某个名词「从哪来、是什么」说不清,去《SSO 概念课》对应的那一页;
- 「你能做什么」那一半的问题——权限模型、数据范围、租户隔离——去
《权限与多租户》,
它的开篇把认证明确划了出去。
前置要求
- 能读懂 HTTP 请求与响应:会用「请求方法 + 状态码 + header」描述一次交互;
- 在浏览器里看过一次真实的 SSO 跳转(地址栏里出现过
?code=...之类的东西); - 会读 JSON,能用
base64url的思路理解「三段被点号分开的字符串」; - 建议手边有一个能跑 Python 3 的环境——
course/project/的示例都只用标准库。