KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

课程设计说明 · SSO 应用课 — keel 龙骨

SSO 工程课:协议、令牌与会话 的参考信息:课程设计说明 · SSO 应用课

为什么主线是「一次登录的来回」而不是「一堆协议名词」

认证这一层的知识,天然长成两张网:一张是名词网(OIDC / OAuth / SAML / 断言 / realm / claim),
一张是时序网(谁在什么时候把什么交给谁、什么只能用一次)。

按名词开课的结果,读者会背下定义却接不进系统——这正是本课要解决的读者卡点
(知道名词,接进去之后「重定向回来一片空白」)。所以本课把主线定成一次登录的来回:
每章都在同一条链路上加一层东西,加完就断一次、看它从哪里断。

01 选型:这条链路该走哪条协议        (站在链路外面看)
02 走通:GET 授权端点 → 登录 → 回跳 → 换 token
03 拆开:换回来的三个 token 各管什么,每一项校验防什么
04 换个视角:为什么第二个应用不用再登录,登出要清哪两处
05 抬视角:一个应用接多家 IdP 时,链路哪里会断
06 破坏:把前面所有结论当作已知,主动制造五类故障
07 替换:逐条把教学替身换成真实依赖

判断力的落点因此在 05、06、07 三章:选型、排障、上线。
02–04 是它们的证据基础,不是终点。

目标能力(读者学完能做什么)

能力 判断依据
选型 拿到一个 SSO 需求,能说出该走 OIDC 还是 SAML,并说出这个判断依赖哪几个事实(谁主导、客户端类型、要不要前端直接拿令牌、有没有既有企业 IdP)
读报文 给出一次登录的请求 URL 与回跳 URL,能逐个参数说出它是谁生成的、用来防什么
拆令牌 给出一个 ID Token,能说出六项校验(签名、iss、aud、exp、nonce、at_hash)各自漏了会出什么事
定边界 能说出「单点登录成立」的判据是什么、它在什么条件下不成立(换浏览器、清 cookie、会话过期)
做取舍 能说出 access token 该不该发给前端、角色信息该不该塞进令牌,以及两条路各自的代价
排障 登录失败时按「症状 → 去哪看 → 根因」的顺序定位,而不是逐个配置项试
上线 能列出教学单实例与真实部署之间必须替换的点,并说出每个点不换会出什么事

评估证据(每章靠什么证明学会)

本章的「证据」都要求可观察,不要求读者同意课程的判断:

章 可观察的验收物
01 对三个真实场景各给一条协议选择 + 一句「为什么不是另一条」;说清本课与《权限与多租户》的分工
02 跑出一次完整登录:第一步 GET 是 200 登录页、POST 后是 302 带四个参数、换 token 是 200;再造一次 PKCE 失败,观察失败之后正确 verifier 也换不回来
03 对同一个 ID Token 说出每一项 claim 的作用;把 access token 与 ID Token 的 aud 对上(一个是 client_id、一个是 account)
04 用「第二个应用第一步 GET 的状态码」判定单点登录成立;登出前后对比 cookie,指出哪一个的消失代表「已登录」被作废
05 给出一个应用的「多 IdP 路由表」(按什么区分、claim 怎么映射、账号怎么关联),并说出 sub 为什么不能直接当账号
06 复现五类故障中的至少三类,每一条写出「症状 → 去哪看 → 根因」
07 把「教学替身 → 真实替换点」表填完整,并对每一项写出一句「不换会出什么事」

课程内的自测题只考察因果(为什么、漏了会怎样),不考察名词记忆。

教学替身与生产替换点

教学替身(本课/配套项目) 真实依赖 不换会出什么事
MiniOP 进程内实现(course/project/src/minioidc/op.py) 真实 IdP(Keycloak / 企业既有 IdP / 托管身份服务) 单实例没有高可用,IdP 一停所有应用都登不进;也没有审计、账号生命周期、反自动化
HS256 对称签名(标准库无 RSA 时的选择) RS256 / ES256 非对称签名 + 公开 JWKS 校验密钥即签发密钥:任何持有它的组件都能伪造身份
固定 kid 与内存密钥环 轮换中的密钥集(新旧并存,随 JWKS 公布) 换密钥即全体掉线;或者旧密钥永久有效,等于轮换没发生
内存 codes / sessions 字典 共享缓存/数据库 + TTL 与并发控制 多副本部署时 code 在 A 副本签发、B 副本认不出;会话在重启后消失
固定时钟(Clock) 服务器时间同步(如 NTP) 时钟偏移会让 exp / iat 判断出现整段整段的误判
Secure; SameSite=None 写在 http://localhost 上 HTTPS + 正确的 cookie 属性 脚本与浏览器行为不一致:表现为「登录页能开、一提交就说会话不存在」
应用侧 Browser 类(管 cookie) 真实浏览器 + 前后端分离场景下的存储选择 令牌放进 localStorage 与放进 HttpOnly cookie 的暴露面完全不同
内存里的 refresh_tokens 映射 刷新令牌的轮换 + reuse detection(可选开启) 默认只轮换、旧令牌仍可用:令牌被窃取后不会被发现
单 realm 单 issuer 多 realm / 多 IdP / 代理后的 issuer 一致性 iss 字面量不一致时,现象与「签名不对」一模一样

与已有课程的分工

已有课程 本课只负责
《权限与多租户》 那边是授权(你能做什么)+ 数据隔离;本课是认证(你是谁)+ 身份怎么在系统间传递。05 章在「跨系统身份 vs 租户内身份」这一处与它对齐,其余不重复
《SSO 基础课》 那边负责「起一个 IdP、把一个应用接进去」的操作面;本课假定这些已经做过,直接讲报文、约束与故障
《SSO 概念课》 那边是每个名词「从哪来、是什么」的概念页;本课在每个名词第一次出现时只给一句够做判断的定义,并把概念页作为下钻入口

防止课程变成玩具的标准

每条结论都要能被一次故意破坏验证,做不到的结论不写进正文:

这些断言都在 course/project/tests/ 里有对应测试,失败路径断言的是具体 error 串。

进入 keel 阅读