3. OAuth2.0 基础回顾:四种授权模式与 Token 类型

OAuth2.0 是目前业界标准的授权协议,它允许第三方应用在用户授权的前提下,安全地访问受保护资源,而无需暴露用户的长期凭证(如密码)。理解 OAuth2.0 的四种授权模式以及 Token 类型,是构建安全、可扩展后端缓存体系的前提。

3.1 四种授权模式对比

OAuth2.0 根据客户端类型(机密/公开)和信任级别,定义了四种授权模式。下表从安全性、适用场景和 Token 获取流程三个维度进行对比:

授权模式 客户端类型 安全性等级 典型场景 是否需要 Refresh Token
授权码模式 机密客户端(有后端服务器) 最高 Web 应用、移动端原生应用(通过 PKCE 增强) 是
隐式模式 公开客户端(纯前端) 低(已不推荐) 单页应用(SPA,已被授权码+PKCE 取代) 否(直接返回 Access Token)
密码模式 高度信任的客户端 中 第一方应用、遗留系统迁移 是
客户端凭证模式 机密客户端(无用户参与) 高(仅限机器间通信) 微服务间调用、后台任务、API 网关 视 Token 有效期而定
缓存一致性提示: 授权码模式由于涉及前端、后端和授权服务器三方交互,其 Token 的缓存生命周期管理最为复杂。在分布式缓存中,授权码的临时存储和 Access Token 的刷新策略需要严格同步,否则会导致“授权码已过期但缓存未清除”或“Token 已刷新但旧 Token 仍在缓存中”的不一致问题。

3.2 授权码模式(核心模式)

授权码模式是 OAuth2.0 中最安全、最完整的授权流程。它通过一个临时的“授权码”作为中间凭证,确保 Access Token 只在安全的后端通道中传输,避免了 Token 在前端暴露的风险。

下图展示了授权码模式的标准交互流程:

OAuth2.0 授权码模式流程 授权码模式(Authorization Code Flow) 用户代理(浏览器) 客户端应用(后端) 授权服务器 资源服务器 ① 访问资源 ② 重定向至授权端点 ③ 用户认证并授权 ④ 返回授权码(code) ⑤ 客户端用授权码 + client_secret 换取 Token ⑥ 返回 Access Token + Refresh Token ⑦ 使用 Access Token 请求资源 ⑧ 返回受保护资源 关键点:授权码是一次性的,且必须与客户端密钥(client_secret)配合使用,确保 Token 只在安全后端传输。
缓存一致性关键点: 在步骤④中,授权服务器返回的授权码(code)通常需要缓存到 Redis 等分布式缓存中,并设置极短的 TTL(如 60 秒)。步骤⑤中,客户端用授权码换取 Token 后,必须立即从缓存中删除该授权码,防止重放攻击。如果缓存删除失败或延迟,将导致授权码被重复使用,引发安全漏洞。

3.3 隐式模式(已不推荐)

隐式模式是授权码模式的简化版,它直接返回 Access Token,跳过了授权码交换步骤。由于 Access Token 直接暴露在 URL 片段中,存在被浏览器历史记录、Referer 头等泄露的风险。目前业界已普遍使用授权码模式 + PKCE(Proof Key for Code Exchange)来替代隐式模式。

安全警告: 隐式模式不返回 Refresh Token,且 Access Token 直接暴露在前端。在分布式缓存设计中,如果仍使用隐式模式,必须将 Access Token 的缓存 TTL 设置得非常短(如 5 分钟),并配合前端定时刷新机制,但这会显著增加授权服务器的负载和缓存一致性的维护难度。

3.4 密码模式

密码模式允许客户端直接使用用户的用户名和密码换取 Token。这种模式要求客户端高度可信(通常是第一方应用),因为它直接接触用户的明文密码。在微服务架构中,密码模式常用于内部服务间的快速集成,但应尽量避免在第三方应用中使用。

// 密码模式 Token 请求示例
POST /oauth/token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=password
&username=user@example.com
&password=secret_password
&client_id=your_client_id
&client_secret=your_client_secret
缓存策略: 密码模式获取的 Access Token 通常具有较长的有效期(如 24 小时)。在缓存中,需要将 Token 与用户 ID 建立映射关系,以便在用户主动登出或密码变更时,能够批量失效该用户的所有 Token 缓存。

3.5 客户端凭证模式

客户端凭证模式是唯一不需要用户参与的授权模式。客户端使用自己的身份凭证(client_id + client_secret)直接向授权服务器申请 Token。这种模式适用于微服务之间的内部通信、定时任务、后台批处理等场景。

// 客户端凭证模式 Token 请求示例
POST /oauth/token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=service-a
&client_secret=service-a-secret
&scope=read:orders write:orders
缓存一致性挑战: 在微服务架构中,多个服务实例可能同时使用相同的 client_id 请求 Token。如果每个实例都独立缓存 Token,会导致缓存冗余和一致性问题。最佳实践是使用集中式缓存(如 Redis)存储客户端凭证 Token,并设置合理的 TTL(通常为 Token 有效期的 80%),在 Token 即将过期时提前刷新,避免服务调用因 Token 过期而失败。

3.6 Token 类型详解

OAuth2.0 定义了两种核心 Token 类型:Access Token 和 Refresh Token。它们在生命周期、用途和缓存策略上存在显著差异。

属性 Access Token Refresh Token
用途 访问受保护资源 获取新的 Access Token
有效期 短(通常 15 分钟 - 1 小时) 长(通常 7 天 - 30 天)
存储位置 客户端内存或安全存储 客户端安全存储(如 HttpOnly Cookie)
缓存策略 高频访问,需快速失效 低频访问,需持久化存储
失效方式 过期、用户登出、权限变更 过期、用户登出、Refresh Token 轮换
缓存一致性要求 高(需实时同步失效) 中(可容忍短暂不一致)

下图展示了 Access Token 与 Refresh Token 在缓存中的生命周期管理:

Access Token 与 Refresh Token 缓存生命周期 Token 缓存生命周期管理 时间轴 Access Token 有效 过期 Refresh Token 有效(长期) 触发 Refresh 返回新 Access Token 新 Access Token 有效 缓存策略:Access Token 缓存 TTL 设置为有效期的 80%,在 TTL 到期前主动刷新,避免服务调用中断。 Refresh Token 缓存 TTL 设置为有效期的 90%,并在刷新时进行轮换(旧 Refresh Token 失效)。

3.7 缓存一致性设计原则

基于上述四种授权模式和 Token 类型,后端缓存一致性设计应遵循以下原则:

  • 授权码缓存: 使用 Redis 等分布式缓存,设置极短 TTL(30-60 秒),使用后立即删除,防止重放攻击。
  • Access Token 缓存: 设置 TTL 为 Token 有效期的 70%-80%,在 TTL 到期前通过后台任务或懒加载方式主动刷新。
  • Refresh Token 缓存: 设置 TTL 为 Token 有效期的 90%,并实现 Refresh Token 轮换机制(每次刷新返回新的 Refresh Token,旧 Token 立即失效)。
  • 缓存失效广播: 当用户主动登出、密码变更或权限更新时,通过消息队列(如 Redis Pub/Sub 或 Kafka)广播 Token 失效事件,确保所有缓存节点同步清除相关 Token。
  • 客户端凭证缓存: 在微服务网关层实现集中式 Token 缓存,避免每个服务实例独立缓存导致的冗余和一致性问题。
常见陷阱: 不要将 Access Token 和 Refresh Token 存储在同一个缓存键中。如果 Refresh Token 因缓存过期被误删,用户将被迫重新登录。建议使用不同的键前缀(如 access_token:{user_id} 和 refresh_token:{user_id})分别管理。