外部 SSO 身份到 DiyToken 权限链路封面

一条登录链路里,认证与授权是两个连续但不能互换的问题。

摘要| 外部身份中心能证明“这个人是谁”,却不能替 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 与活动用户、角色、部门、菜单动作、表权限和数据范围共同决定一次请求是否允许。前端隐藏按钮只是体验,不能代替服务端检查;外部角色名也不能直接换成平台管理员。

  1. 先确认 Token 中的 OsClient 与当前请求租户一致。
  2. 再从服务端权限快照确认用户、有效角色和目标菜单。
  3. 列表、写入、导入、导出分别检查动作权限。
  4. 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 资源同源和安全默认值。

SSO聚焦测试33项全部通过

测试结果来自当前源码;复用了现有构建,没有停止共享服务。

验收边界| 聚焦测试不等于真实伙伴验收。上线还要验证浏览器弹窗、真实 Keycloak/Entra/ADFS/CAS、拒绝与重放、用户停用、密钥轮换、多节点票据和缓存一致性。

所以,可靠 SSO 的结论不是“外部 Token 能登录”,而是:外部协议只建立可信身份,平台用一次性票据把身份交给 DiyToken,再由统一权限系统决定每一次业务动作。认证与授权保持连续,也保持各自独立。

本文技术概念卡片底图由AI生成并经确定性程序叠加准确中文;长文信息图、源码分析与本地测试证据来自当前工作区,概念图不冒充运行证据。

Logo

宁波官方开源宣传和活动阵地,欢迎各位和我们共建开源生态体系!

更多推荐