2、令牌生命周期管理:Access Token与Refresh Token的生成、过期策略、刷新机制及安全存储要求

令牌生命周期管理是OAuth 2.0授权体系的核心安全防线。Access Token与Refresh Token承担不同职责:前者用于快速验证资源访问权限,后者用于无感续期。本章从生成、过期、刷新到存储,逐层拆解令牌全生命周期的设计要点与工程落地规范。

2.1 令牌类型与职责划分

OAuth 2.0定义两种核心令牌,其生命周期策略必须差异化设计:

维度 Access Token Refresh Token
用途 携带至资源服务器,证明客户端有权访问受保护资源 在Access Token过期后,向授权服务器换取新的Access Token
有效期 短(通常15分钟~1小时) 长(通常7天~30天,甚至更长)
传输频率 每次API请求均携带 仅在令牌刷新时传输
泄露影响 攻击者可在有效期内冒充用户访问资源 攻击者可长期续期,持续窃取访问权限
存储要求 内存/短期缓存,避免持久化 安全存储(如HttpOnly Cookie、Keychain)
设计原则:Access Token应“短命且无状态”,Refresh Token应“长命但有状态”。授权服务器必须维护Refresh Token的状态(如是否已撤销、是否单次使用),而Access Token可完全依赖签名验证,无需服务端存储。

2.2 令牌生成规范

令牌的生成必须满足不可预测性、防篡改、可验证三个安全属性。以下是两种主流生成方式:

2.2.1 自包含令牌(JWT)

JWT(JSON Web Token)将用户身份、权限范围、过期时间编码在令牌本身,资源服务器无需回查授权服务器即可完成验证。典型Payload结构:

{
  "iss": "https://auth.example.com",
  "sub": "user_12345",
  "aud": "https://api.example.com",
  "exp": 1718000000,
  "iat": 1717996400,
  "scope": "read write",
  "client_id": "web_app_01"
}
  • 签名算法:推荐使用RS256(RSA-SHA256)或ES256(ECDSA-SHA256),避免使用HS256(对称密钥)在分布式环境下泄露密钥。
  • JTI(JWT ID):为每个Access Token生成唯一标识,用于黑名单或日志追踪。
  • 避免敏感数据:JWT Payload仅用Base64编码,非加密,严禁放入密码、手机号等PII。

2.2.2 不透明令牌(Opaque Token)

不透明令牌是一串随机字符串(如UUIDv4或CSPRNG生成的32字节序列),授权服务器需维护令牌到用户信息的映射表。适用于需要服务端即时撤销的场景。

// 生成示例(Node.js crypto)
const crypto = require('crypto');
const opaqueToken = crypto.randomBytes(32).toString('hex');
// 输出: "a1b2c3d4e5f6...(64位十六进制字符串)"
选型建议:高并发、低延迟场景优先选择JWT(无状态验证);需要即时撤销、令牌轮换、审计追踪的场景优先选择Opaque Token(有状态管理)。

2.3 过期策略设计

过期策略直接影响系统安全性与用户体验。以下是三种常见策略及其适用场景:

策略 Access Token有效期 Refresh Token有效期 适用场景
短生命周期 5~15分钟 1~7天 金融、支付、高安全敏感系统
中等生命周期 30分钟~1小时 7~30天 企业SaaS、电商平台
长生命周期(受限) 1~2小时 30~90天 IoT设备、后台服务间通信
安全警告:Refresh Token有效期超过30天时,必须启用“令牌轮换”(Token Rotation)机制——每次刷新时同时颁发新的Refresh Token并废弃旧令牌,防止长期令牌泄露导致权限永久丢失。

2.4 刷新机制与令牌轮换

刷新流程是令牌生命周期管理的核心交互环节。下图展示了标准刷新流程与令牌轮换的完整链路:

Access Token 刷新与令牌轮换流程 客户端 授权服务器 资源服务器 ① 携带Access Token请求API ② 返回401(令牌过期) ③ 发送Refresh Token请求刷新 ④ 返回新Access Token + 新Refresh Token ⑤ 使用新Access Token重试请求 ⑥ 返回受保护资源 令牌轮换关键规则: • 每次刷新时,授权服务器同时废弃旧Refresh Token,颁发全新Refresh Token • 若检测到已废弃的Refresh Token被使用,立即撤销该用户所有令牌并触发安全告警

2.5 安全存储要求

令牌存储位置直接决定泄露风险等级。以下是不同客户端类型的安全存储规范:

客户端类型 Access Token存储 Refresh Token存储 安全说明
浏览器SPA 内存变量(闭包/模块作用域) HttpOnly + Secure + SameSite=Strict Cookie 禁止localStorage/sessionStorage存储任何令牌
原生移动App 内存或系统安全存储(iOS Keychain / Android EncryptedSharedPreferences) 系统安全存储 + 设备绑定(Key Attestation) 避免使用SharedPreferences明文存储
后端服务 进程内存或分布式缓存(Redis) 加密持久化(AES-256-GCM) 缓存设置TTL与令牌过期时间一致
IoT/嵌入式设备 安全元件(SE)或TEE隔离区 安全元件 + 硬件唯一密钥加密 禁止明文写入Flash文件系统
工程实践:在SPA中,将Access Token保存在JavaScript闭包变量中,配合BFF(Backend for Frontend)模式,由后端代理完成令牌刷新,前端仅持有短期会话Cookie,从根本上消除XSS窃取令牌的风险。

2.6 令牌生命周期状态机

从颁发到废弃,令牌经历多个状态转换。下图展示了完整的生命周期状态机:

令牌生命周期状态机 已颁发 活跃 已过期 已撤销 已轮换 颁发 超时 刷新轮换 用户撤销 轮换后废弃

2.7 关键工程要点总结

  • 令牌绑定:将Access Token与客户端ID、IP段或设备指纹绑定,降低令牌被盗用后的影响范围。
  • 刷新窗口:在Access Token过期前5%~10%的时间窗口内,客户端应主动发起静默刷新,避免请求中断。
  • 并发刷新保护:使用锁机制或去重队列,防止多个请求同时触发刷新导致令牌竞争条件。
  • 审计日志:每次令牌颁发、刷新、撤销操作均记录日志,包含时间戳、客户端ID、用户标识、令牌指纹(JTI)。
  • 定期轮换密钥:JWT签名密钥至少每90天轮换一次,旧密钥保留一个过期窗口用于验证未过期令牌。
常见陷阱:不要将Refresh Token设计为“永久有效”。即使业务需要长期登录,也应设置最大绝对过期时间(如90天),到期后要求用户重新认证。同时,禁止在URL参数中传递任何令牌,防止日志泄露和中间人攻击。