SSO登录成功,为什么还不能直接信任外部Token?

一条登录链路里,认证与授权是两个连续但不能互换的问题。
摘要| 外部身份中心能证明“这个人是谁”,却不能替 Microi 吾码决定“这个人能看哪张表、按哪个按钮、修改哪一行”。可靠的链路必须经过协议验真、租户主体解析、90 秒一次性票据、DiyToken 签发,再进入角色、部门、菜单、表和数据范围授权。
✦ ① 登录成功,只回答了第一个问题
很多 SSO 集成在拿到 OIDC 的 id_token、SAML Assertion 或 CAS Ticket 后就宣布完成:签名有效、用户邮箱也对,于是直接把外部角色塞进平台。这里混淆了两件事。外部身份提供方回答的是“这个主体是谁”;平台还必须回答“当前租户是否认识他,以及他现在能做什么”。
边界| 外部 Token 不能直接变成菜单、按钮、表、字段或行级权限。它甚至不能跳过对当前 sys_user 是否停用、删除或换角色的重新读取。

协议回调、主体解析、一次票据、DiyToken、资源授权,缺一段都不是完整登录。
✦ ② 协议网关只做不可伪造的验真
OIDC 要核对授权码、state、nonce、PKCE、签名、issuer、audience 和有效期;SAML 要核对签名、Destination、Audience、InResponseTo 与重放;CAS Ticket 必须单次使用并绑定精确 service。这些工作靠近原始报文、证书和 Secret,属于最小可信协议边界。
- 回调地址必须精确匹配,不能接受前缀、通配或 URL fragment。
- 生产身份端点使用 HTTPS,私网身份源只能显式受控放行。
- 失败日志不记录 code、Token、Secret、断言原文或 DiyToken。
外部协议凭据 → 验签/验端点/验时效
→ 归一化 Claim(不含原始 Token)
→ sso_resolve_federated_identity
✦ ③ 身份解析为什么放进 Managed V8
协议验真完成后,网关只把归一化后的 Connection 与 Profile 交给 `sso_resolve_federated_identity`。
绑定、BoundOnly/JitMatch/JitCreate、Claim 映射、角色映射和审计都是可升级的业务编排,更适合由官方 Managed 接口引擎交付,而不是在 OIDC、SAML、CAS 三套 Controller 中复制。
隔离键| 稳定主体以 OsClient + ConnectionKey + Subject 为边界。同一个 Subject 出现在另一租户或另一连接上,不能自动继承原绑定。
var identity = V8.ApiEngine.Run(
'sso_resolve_federated_identity',
{ Connection: safeConnection, Profile: normalizedProfile }
);
// 默认 BoundOnly;JIT 必须配置非管理员默认角色与回收策略
✦ ④ 90 秒票据不是另一个登录 Token
身份解析得到启用用户后,系统生成高熵、租户绑定、约 90 秒有效的一次性票据。浏览器弹窗只把这张票据送回原始可信 Origin,登录页再用它完成登录。票据通过 Redis 原子删除消费:不存在、过期或已经使用,结果都失败关闭。
为何必要| 它把“外部回调已验真”与“平台会话即将签发”隔开,避免把 DiyToken 放进 Query、fragment 或 `postMessage('*')`,也让重放变成可检测状态。

外部 Token、一次票据、DiyToken、权限快照分别承担不同责任。
✦ ⑤ DiyToken 仍是唯一平台会话入口
`sso_complete_login` 消费票据后,会按当前租户重新读取 `sys_user`,确认 `State=1` 且未删除,再补齐角色信息并签发 DiyToken。SSO、密码、Passkey、人脸或其它身份入口最终都汇入同一会话体系,避免出现第二套用户、角色、有效期和撤销规则。
ticket = Redis.GETDEL(tenantTicketKey)
assert ticket.OsClient === currentOsClient
user = rereadEnabledSysUser(ticket.UserId)
return issueDiyToken(user, clientType, did)
别误会| DiyToken 也不是“任意表通行证”。它确认当前用户、租户和终端会话,具体资源仍按服务端权限快照逐次授权。
✦ ⑥ 真正的授权发生在登录之后
进入平台后,DiyToken 与活动用户、角色、部门、菜单动作、表权限和数据范围共同决定一次请求是否允许。前端隐藏按钮只是体验,不能代替服务端检查;外部角色名也不能直接换成平台管理员。
- 先确认 Token 中的 OsClient 与当前请求租户一致。
- 再从服务端权限快照确认用户、有效角色和目标菜单。
- 列表、写入、导入、导出分别检查动作权限。
- SqlWhere/SqlJoin 进入真实列表、计数和导出查询;行级写限制由可信后端事件或接口引擎检查。
即时收窄| 用户停用、角色变化、菜单权限修改和全部终端吊销都应让共享会话/授权事实失效,而不是等外部 Token 自己过期。
✦ ⑦ 薄 C# 与 V8 编排各守哪条线
Microi吾码AI 的这条实现不是追求“所有代码都放在一种语言里”,而是把边界放对:C# 保留原始协议报文、签名验签、Secret/证书隔离、一次性票据与 DiyToken 原子;V8 接口引擎承载连接投影、身份绑定/JIT、角色与 Claim 映射、审计和登录完成编排。
- 可信原子只能由精确 Managed ApiEngineKey 调用,普通 V8 不能指定租户或主密钥。
- 官方核心接口使用 Managed;租户扩展 Hook 使用 CreateIfMissing,安装后不覆盖。
- 固定客户端调用 `/apiengine/{Key}?OsClient=`,让日志与监控按真实接口引擎归因。
可升级性| 协议内核随 Server 版本升级,业务编排随 `app.microi.sso` 应用包升级;两条线分开,既不泄露可信能力,也不把租户定制锁死在 Controller。
✦ ⑧ 33 项通过,为什么仍不能宣布生产验收
本轮用当前工作区现有 Debug 构建跑了 22 项 SSO 安全测试,并跑了 11 项官方应用包合同测试,合计 33/33 通过。覆盖了精确 Redirect URI、PKCE S256、租户/Client/User 隔离、Secret 不落明文、Managed 资源同源和安全默认值。

测试结果来自当前源码;复用了现有构建,没有停止共享服务。
验收边界| 聚焦测试不等于真实伙伴验收。上线还要验证浏览器弹窗、真实 Keycloak/Entra/ADFS/CAS、拒绝与重放、用户停用、密钥轮换、多节点票据和缓存一致性。
所以,可靠 SSO 的结论不是“外部 Token 能登录”,而是:外部协议只建立可信身份,平台用一次性票据把身份交给 DiyToken,再由统一权限系统决定每一次业务动作。认证与授权保持连续,也保持各自独立。
本文技术概念卡片底图由AI生成并经确定性程序叠加准确中文;长文信息图、源码分析与本地测试证据来自当前工作区,概念图不冒充运行证据。
更多推荐




所有评论(0)