从 Ticket-v1 到 OIDC:一场跨越十年的身份认证架构演进与平滑迁移实战

场景开场
如果你接手过一个用了七八年的老系统,大概率见过这样的场面:认证中心是十年前搭的 CAS,业务侧从单体 Web 长成了几十个微服务,又冒出小程序、App、API 网关。运维半夜被告警吵醒,一看是认证中心 /serviceValidate 被打爆了,因为每个微服务、每次跳转都要回中心校验一次 Ticket。想升级到 OIDC,又不敢动,因为老应用一堆 CAS Client SDK,牵一发动全身。
这篇文章不讲协议扫盲,只解决一件事:Ticket-v1 和 OIDC 到底差在哪,为什么现在必须迁,以及怎么在不停业务的前提下平滑切过去。后面有一份四步迁移清单和两个真实踩坑点,建议先收藏,真到要动手改造的时候,能直接拿去对着做。
一句话结论
先把结论摆前面,省得你读到一半划走:
Ticket-v1 是有状态的反向校验,OIDC 是无状态的本地验签。前者把校验压力全压在认证中心,后者把校验能力下放到每个应用。这一个差别,决定了 Ticket-v1 天然扛不住微服务和高并发,而 OIDC 天生就是给分布式架构准备的。
所以问题从来不是"OIDC 好不好"而是"你还要不要继续把认证中心当单点瓶颈养着"选型的时候记住一句话:新项目直接 OIDC,老系统别硬切,用协议叠加 + 分批迁移。
两个时代
Ticket-v1 出生在浏览器还只会做重定向的年代。那时候的 SSO,逻辑很朴素:用户在认证中心登录成功,中心发一张短效随机字符串(比如 ST-12345-x)给浏览器,浏览器带着这张票跳回业务系统,业务系统再悄悄回中心问一句"这张票是不是真的、是谁的"票用完就作废,跟你去电影院检票一个流程。
OIDC 是完全另一个思路。它建立在 OAuth 2.0 之上,颁发的是一个 ID Token,本质是一个用私钥签过名的 JWT。里面直接写好了用户是谁、什么时候过期、有哪些属性。应用拿到之后不需要再问中心,用中心公开的公钥(JWKS)在本地验签就行。
一个像纸质门票,检票员必须一张张核;一个像带防伪芯片的电子票,闸机自己就能刷。两者不是简单的新旧,而是从"中心化查验"到"去中心化信任"的架构转向。

校验机制
这是两者最要命的差别,也是很多老系统扛不住并发的根源。
Ticket-v1 的校验路径:
- 用户登录成功,认证中心把 Ticket 塞给浏览器。
- 浏览器带着 Ticket 跳回业务系统。
- 业务系统必须再发一次 HTTP 请求到认证中心的
/serviceValidate接口,问这张票有效吗、是谁。 - 中心校验成功后返回用户 ID,同时把这张 Ticket 立刻销毁。
看上去还行?把它放到 50 个微服务、每次跨服务调用都需要身份传递的场景下,认证中心瞬间变成整个系统的心脏起搏器:它一停,全公司登录全挂。
OIDC 的校验路径:
- 用户登录成功,中心颁发一个签名过的 ID Token(JWT)。
- 应用拿到 Token,用中心公开的 JWKS 公钥在本地直接验签。
- 只要签名对、没过期、issuer 和 audience 匹配,就直接信任里面的用户信息。
中心只需要在启动时被拉一次公钥,之后应用端可以脱网校验成千上万次请求。这就是为什么 API 网关(Kong、APISIX、Envoy)都原生支持 OIDC,但基本没人给 Ticket-v1 做插件——它压根不适合网关场景。

生态差距
光有协议差异还不够,真正让 OIDC 碾压式领先的是它的标准化生态。
Ticket-v1 时代,每家厂商的返回格式都不一样:有的返回 XML,有的返回一段拼字符串的伪 JSON,有的连 HTTP 状态码都乱用。接入一个新系统,第一件事是问对方要接口文档,接下来两天都在处理格式差异。
OIDC 把这些都规范化了,几个标准端点你记住就行:
/.well-known/openid-configuration:自动发现,一个 URL 拿到所有端点配置。/authorize:登录入口。/token:换取 Token。/jwks:公钥集合,验签用。/userinfo:补充用户信息。
结果就是 Spring Security、Keycloak、Auth0、Authing、APISIX、Kong 全都开箱即用。你在配置文件里填一个 issuer URL,其余的端点、公钥、算法它自己去发现。老 CAS 那套自定义协议,光让新同事看懂就得花半天。
错误姿势
讲迁移方案前,先说个反面案例。见过不止一个团队,一拍脑袋决定"这个季度全部切到 OIDC"然后三个月后灰头土脸滚回来:
- 老应用几十个,CAS Client SDK 各种版本混用,改动全部代码得连测两个月。
- 一次性切换那天,某个边缘系统忘了升级,用户登录后一直白屏。
- 用户属性对不齐,新应用拿到 JWT 里没有
dept_id,业务逻辑直接崩。 - 单点注销跟老 CAS SLO 不兼容,用户以为退出了,结果另一个页面还是登录态。
如果你们线上也在计划这种"大爆炸式切换"建议对照后面这套方案再评估一下。很多迁移事故不是技术不行,是节奏没控好。认证系统这种东西,一旦影响所有员工登录,回滚成本比想象中高得多。
四步迁移
正确的姿势是"IdP 叠加 OIDC 协议 + 应用端分批渐进"四步走,每一步都可以独立验证、独立回滚:
第一步:认证中心叠加 OIDC 协议层
不动老的 CAS 校验接口,在中心侧扩出 OIDC 能力:
- 用 Apereo CAS 的话,直接开
cas-server-support-oidc模块。 - 自研中心,就加一层 OIDC Adapter,把
/authorize、/token请求转换到内部登录逻辑。 - 老中心动不了,就前面挂一个 Keycloak,把上游身份源指向老 CAS。
第二步:打通全局会话
这一步是无感 SSO 的关键。让 OIDC 流程和 CAS 流程共用同一个全局会话 Cookie(比如 CASTGC)。用户访问 OIDC 新应用,中心检查到全局会话,直接免密颁发 ID Token;访问老应用同理,直接免密发 Ticket。
第三步:应用端分批切换
- 新应用、微服务、App、网关一律直接上 OIDC。
- 老应用逐个替换 CAS Client SDK 为标准 OIDC Client。
- 完全动不了的祖传系统,在 API 网关层做 OIDC 认证,把用户信息通过
X-User-Id这类 Header 透传给后端。
第四步:审计并下线 Ticket 接口
盯 /serviceValidate 的访问日志,确认连续几周没有任何调用了,再正式下线老模块。不要凭感觉,一定要凭日志。
看到这里,如果你们团队正好在做类似改造,建议把这四步单独拎出来对齐一下。很多迁移拖成一年半,不是技术问题,是没人把节奏切成可交付的小步。

网关兜底
单独拎出来讲第三步里最实用的一招:用 API 网关兜底老系统。
很多公司有那种"改不动、也没人敢改"的老服务,代码是外包写的,人早跑了。这类系统直接上 OIDC 客户端改造不现实,但你可以把它挡在网关后面:
- 网关(APISIX / Kong / Envoy Gateway)配置 OIDC 插件,负责跟认证中心走标准协议。
- 网关拿到 ID Token,本地验签,解出用户信息。
- 通过自定义 Header(例如
X-User-Id、X-User-Roles)注入到后端请求里。 - 老服务继续用它熟悉的方式从 Header 读用户 ID,一行代码不用改。
注意一个细节:网关到后端这段链路必须是内网可信的,不然攻击者伪造 Header 就直接绕过认证了。生产环境里,要么走内网 VPC,要么在网关和后端之间加一层 mTLS。这个坑我见过团队栽过,不点名。
这段建议转给做网关和运维的同事,网关侧其实能替业务侧扛掉一大半迁移工作量,前提是配置得对。

坑一:SLO
迁移里最容易被忽视的一个大坑:单点注销机制不一致。
老 CAS 的 SLO 是这么干的:用户在中心退出后,中心主动往每个业务应用发一个 HTTP POST,通知它们清 Session。这套机制的前提是——中心得知道每个应用的回调地址,而且这些地址得在网络上可达。
OIDC 的做法完全不同,它有几种:
- Front-Channel Logout:通过 iframe 加载各应用的登出 URL,让浏览器帮忙广播。
- Back-Channel Logout:中心直接向应用后端发 logout token。
- 短 Token + Refresh Token:干脆不做同步注销,让 Token 自然过期。
迁移过渡期新旧混跑,最容易出现"用户以为退出了,其实另一个页面还是登录态"的诡异现象。给你一个务实建议:
过渡期先把 ID Token 有效期调短,比如 5 到 15 分钟,配合 Refresh Token 做续期。
这样即使同步注销失效,最多十几分钟后 Token 也就自动作废了。等所有应用都切到 OIDC,再统一上 Back-Channel Logout,能少掉一大堆诡异 bug。
坑二:Claims
第二个大坑:用户属性映射对齐。
老 Ticket-v1 时代,很多系统的习惯是从中心只拿一个 username,剩下的信息(部门、角色、工号)自己再调用用户中心接口反查。这种模式下,用户中心的 QPS 一直很高,因为每个业务都在反复问同一个人是谁。
切到 OIDC 是个绝佳的重构机会。你可以在 IdP 侧一次性把常用属性塞进 ID Token 或者 /userinfo 端点里,让业务直接从 JWT 解出来就够用。
建议按 OIDC Standard Claims 规范来对齐,别自己乱起名字:
sub:稳定的用户唯一 ID(这个是必须的,且永远不要用 email 或 username 当唯一 ID,因为它们可能变)。email/email_verified:邮箱和验证状态。preferred_username:展示用的用户名。- 自定义 claims 加前缀,比如
https://yourcompany.com/dept_id、https://yourcompany.com/roles,避免和标准字段冲突。
注意 Token 别塞太胖。见过团队一股脑把所有权限点塞进 JWT,结果 Token 大到 8KB,每次 HTTP 请求 Header 都爆了。原则是:身份属性放 Token,动态权限走 API 查。
上线清单
把这篇里能直接用的东西压缩成一份检查清单,收藏起来,动手改造前对着过一遍:
选型判断
迁移前
迁移中
迁移后
写在最后
认证协议的演进,本质上是架构信任模型的演进:从"什么都要问中心"到"中心只负责签发,剩下的自己验"这一步跨过去,不只是换个协议,是让整个系统的身份层从瓶颈变成基础设施。
如果这篇对你有用,欢迎点个赞。团队里正好有人在推 SSO 升级,也可以顺手转给他,能少绕不少弯路。如果你们踩过比这更复杂的迁移场景,比如多 IdP 联邦、跨集团账号打通,评论区聊聊,我挑几个典型的下次专门写一篇。
我是爱三味,做后端架构做到现在,越发觉得——好的技术方案,从来不是最新的那个,而是能让你在半夜三点睡得着的那个。

老 CAS 单点登录扛不住微服务和 App,直接推倒重来又要牵动几十个业务系统。这篇讲清楚 Ticket-v1 和 OIDC 的本质差别,给出一套可以边跑边换的四步迁移法,附带 SLO 和 Claims 两个最容易翻车的坑。适合正在做统一认证升级的后端和架构同学。
浙公网安备 33010602011771号