土法炼钢兴趣小组的算法知识备份

【身份与访问控制工程】Session、Refresh Token 与吊销体系

文章导航

分类入口
architecturesecurity
标签入口
#jwt#refresh-token#token-revocation#session-management#rfc7009#rfc7662

目录

JWT 一次讲透 中我们讨论了 JWT 的签名和加密机制。但 JWT 最根本的结构性问题不是算法选择,而是吊销:一旦签发,JWT 在到期之前就是一台按规则行驶的无人列车——服务端没有办法让它提前停下。

这个问题不是 JWT 特有的——任何无状态或近似无状态的令牌机制都面临同样的挑战。本文从吊销的场景分类出发,拆解四种主流吊销机制(Token Introspection、Refresh Token Rotation、吊销列表、基于事件的通知),给出工程权衡矩阵。

一、吊销的场景分类

场景 紧急程度 影响范围 需要吊销的令牌
用户修改密码 所有设备 所有该用户的 Access Token + Refresh Token
管理员禁用账号 该用户 所有该用户的 Access Token + Refresh Token
权限变更(降级/换岗) 该用户 Access Token(好让下次获取时缩小 scope)
令牌泄露 最高 单一令牌 泄露的那一个 Refresh Token
用户主动登出 当前设备 当前 Session + 对应的 Refresh Token
设备丢失 该设备 该设备上的所有令牌

场景分析:修改密码和吊销令牌看起来是两件事,但在 JWT 体系下它们是同一个问题——旧的 JWT 不会因为密码改了而自动失效。密码修改后攻击者如果持有修改前的 JWT,仍可访问。

二、Token Introspection(RFC 7662)

Token Introspection 是最直接的吊销方案:RP 每次收到 Access Token 时,不去做本地 JWT 验证,而是把 token 发回 OP 的 Introspection Endpoint,由 OP 告诉 RP “这个 token 还有效吗”。

POST /introspect
Authorization: Basic <client_id:client_secret>
Content-Type: application/x-www-form-urlencoded

token=eyJhbG...access_token&token_type_hint=access_token

→ 200 OK
{
  "active": true,
  "scope": "read write",
  "client_id": "my-app",
  "username": "zhangsan",
  "exp": 1718400000,
  "sub": "user-12345"
}

→ 如果已吊销:
{
  "active": false
}

优点:实时吊销——OP 可以在每次 Introspection 请求时决定 token 是否有效。

代价: - 每次 API 调用都需要向 OP 发起一次网络请求——延迟增加 5-50ms。 - OP 需要承受所有 RP 的所有 API 流量的 Introspection 请求量。 - 需要缓存层:如果对每个 Introspection 查询都做数据库查询,OP 会很快成为瓶颈。

实际使用:绝大多数实现不会对每个 API 请求都做 Introspection,而是结合本地 JWT 验证(短期 Access Token,5-15 分钟)加定期 Introspection(如每 5 分钟一次)。这形成了一种妥协:吊销窗口最长 5 分钟,但 99% 的请求不需要 Introspection 调用。

Token Introspection 和 JWT 的 jti(JWT ID)字段配合使用效果最好——OP 签发 JWT 时写 jti,Introspection 时用 jti 快速索引到令牌的状态记录。

三、Refresh Token Rotation

OAuth 2.1 篇 中已经介绍了 Refresh Token Rotation 的基本原理——每次用 Refresh Token 换新 Access Token 时,OP 返回新的 Refresh Token,旧的立即失效。这里补充工程实现细节:

3.1 Token Family 模型

Rotation 的正确实现不是简单地”每次发新的,旧的失效”,而是维护一个 Refresh Token Family:

Token Family: RT-FAMILY-ABC
  RT-1 (parent) → 签发 RT-2, 使 RT-1 失效
  RT-2 (child of RT-1) → 签发 RT-3, 使 RT-2 失效
  RT-3 (child of RT-2) → ...

如果一个 Refresh Token 被重放(已经被用过了): - 合法客户端持有 RT-3,攻击者持有 RT-1。 - 合法客户端正常使用 RT-3 → RT-4。 - 攻击者使用 RT-1 → RT-1 已经失效(它签发过 RT-2)。 - 此时系统检测到:RT-1 属于 FAMILY-ABC,但其直接后继 RT-2 早已签发——这不是”正常重放”,这是 FAMILY-ABC 中的某个历史 token 被窃取了。 - OP 应该使整个 Token Family 失效(所有 RT-1、RT-2、RT-3、RT-4 全部吊销),因为无法确定攻击者拿到了 family 中的哪些 token。

这称为”Automatic Replay Detection”——它提供了比简单 Rotation 更精确的泄露检测。

3.2 Refresh Token 的存储结构

在 OP 侧,每个 Refresh Token 至少存储:

字段 用途
token_hash Refresh Token 的哈希(用于索引和查询,不存明文)
family_id Token Family 的唯一标识
parent_hash 此 token 是从哪个 parent 签发出来的
user_id + client_id 关联用户和客户端
scope 此 token 可恢复的权限范围
created_at 签发时间
expires_at 过期时间
revoked 是否已吊销
device_info 可选的设备指纹(用于用户查看”哪些设备登录中”)

数据库查询模式:SELECT * FROM refresh_tokens WHERE token_hash = $1,单行查询,通过 token_hash 的 B-Tree 索引主键查找实现。

四、吊销列表

对于不需要 Introspection 的场景(如只需要知道”哪些 token 已被吊销”,而不想知道 token 当前状态),可以使用吊销列表。

本地的吊销列表是一个简单的 Set / Bloom Filter,存储已吊销的 JWT 的 jti

吊销列表(Redis / 本地内存):
  SET revoked_jti: "abc-123", "def-456", "ghi-789"
  
  Access Token 验证流程:
  1. 验证 JWT 签名
  2. 检查 exp, iss, aud
  3. 检查 jti 是否在吊销列表中
  4. 如果在 → 拒绝请求

Bloom Filter 方案适合对内存敏感的部署(如 sidecar 模式下的 Envoy 插件)——允许一定的假阳性(可能拒绝少量合法的已吊销 token,但绝不会放过已吊销的 token)。

工程取舍:吊销列表的维护成本与吊销事件频率成正比。在令牌泄露或大批量员工离职的场景下,吊销列表可能迅速膨胀。需要定期清理过期 token 的 jti(exp 已过的 token 不需要被吊销——它们本来就是无效的)。

五、基于事件的吊销通知

以上三种方案都是”RP 去问”(Pull 模式)。基于事件的吊销是”OP 主动通知”(Push 模式)。

OpenID Shared Signals Framework (SSF)CAEP(Continuous Access Evaluation Protocol)定义了这个机制:

  1. RP 订阅 OP 的安全事件流(SET,Security Event Token)。
  2. 当 OP 检测到安全事件(如”用户密码被重置”“会话被终止”“设备被撤销”),OP 向订阅的 RP 发送 SET。
  3. RP 收到 SET 后,立即使该用户的所有相关 token 失效。
{
  "iss": "https://idp.example.com",
  "iat": 1718396400,
  "jti": "set-event-abc-123",
  "aud": "https://rp.example.com",
  "events": {
    "urn:ietf:params:SCIM:event:passwordReset": {
      "subject": {
        "subject_type": "iss-sub",
        "iss": "https://idp.example.com",
        "sub": "user-12345"
      }
    }
  }
}

优点:延迟最小(事件发生后秒级推送),覆盖面最广。

缺点:依赖 OP 和 RP 双方实现 SSF/CAEP——2026 年这个标准仍处于早期采用阶段。Google 和 Microsoft 有内部使用的等效机制(Google 的 Continuous Access Evaluation),但标准化和跨产品互操作还在推进。

六、四种机制的工程权衡

机制 吊销延迟 性能影响 实现复杂度 覆盖范围
Token Introspection 实时(OP 被查询时) 高(每次 API 调用多一次网络 RTT) 低(标准 RFC) Access Token
Refresh Token Rotation 等于 Access Token 有效期 低(只在 refresh 时触发) 中(需要实现 Token Family) Refresh Token + 间接限制 Access Token
本地吊销列表 等于列表更新频率 低(本地内存查询) 低-中(需要列表同步机制) Access Token(通过 jti)
SSF/CAEP 事件通知 秒级 低(事件驱动) 高(双方需实现 SSF) 全部令牌

生产环境推荐策略

  1. Access Token 有效期 5-15 分钟 + Refresh Token Rotation(基础层)。
  2. 密码修改、账号禁用等关键事件:额外的吊销列表条目(在 Access Token 有效期内提供覆盖率)。
  3. 有条件的团队:实现 Introspection Endpoint 作为备选吊销路径(只在安全事件发生时使用,不作为日常验证路径)。
  4. SSF/CAEP 作为未来方向关注,等待标准成熟。

上一篇JWT、JWS、JWE、JWKS 一次讲透 下一篇MFA、TOTP、WebAuthn、Passkey 工程实践

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-13 · architecture / security

【身份与访问控制工程】IAM 全景:为什么这是高价值赛道

从 2020 年 SolarWinds 到 2024 年 Okta 支持系统泄露,身份基础设施的安全失败反复证明一件事:IAM 不是 IT 支撑系统,而是安全架构的承重墙。本文建立现代 IAM 的全景地图——从认证协议、令牌体系、权限模型到身份治理与平台选型,给出 5 个贯穿全系列的核心问题。

2026-06-13 · architecture / security

【身份与访问控制工程】OAuth 2.1 与 PKCE:现代授权主路径

OAuth 2.1 不是新协议,而是对 OAuth 2.0 的安全加固:废除 Implicit Grant 和 Resource Owner Password Grant,强制 PKCE 用于所有使用授权码模式的客户端,要求精确 redirect_uri 比对。本文从 PKCE 的密码学动机出发,拆解 OAuth 2.1 的授权码流程完整交互、Refresh Token 轮换与发送者约束、DPoP 令牌绑定,以及 DCR (Dynamic Client Registration) 和 RAR (Rich Authorization Requests) 的实际应用。

2026-06-15 · architecture / security

【身份与访问控制工程】JWT、JWS、JWE、JWKS 一次讲透

JWT 不等于 JWS。JWS 是签名格式,JWE 是加密格式,JWK 是密钥表示,JWKS 是密钥集合——这四个规范共同构成了 JOSE(JSON Object Signing and Encryption)技术族。本文从 JOSE 体系全景出发,逐层拆解 JWT 的三段式结构、JWS 的签名算法选择(从 HS256 到 EdDSA 的选择逻辑)、JWE 的密钥加密与内容加密双层模型、JWKS 的密钥轮换与缓存策略。

2026-06-12 · architecture / security

【零信任安全架构】身份感知代理:Google IAP、Pomerium 与零信任的入口

身份感知代理(IAP)是零信任架构中用户进入企业资源的唯一入口——它取代了 VPN 的'拨入内网'模型,把每个 HTTP 请求的认证和授权检查放在资源前面。本文拆解 IAP 协议层的完整请求流、JWT 验证的严格性要求、Header Injection 的安全陷阱,以及 Pomerium/oauth2-proxy/Cloudflare Access 的实现差异。


By .