2. 授权码模式详解:授权码请求、用户授权、授权码发放、令牌交换、刷新令牌、完整序列图

授权码模式(Authorization Code Grant)是 OAuth2 中最安全、最常用的流程,特别适用于有后端服务器的 Web 应用。其核心在于通过一个临时的“授权码”作为中间凭证,避免访问令牌直接暴露在用户代理(浏览器)中。本节将拆解该模式的五个核心阶段,并给出完整的序列图。

2.1 核心参与方与角色

角色 说明 典型示例
资源所有者 拥有受保护资源的用户 最终用户
客户端 需要访问资源的第三方应用 Web 后端服务
授权服务器 负责认证用户并发放令牌 Auth0、Keycloak
资源服务器 托管受保护资源的 API REST API 服务

2.2 阶段一:授权码请求

客户端引导资源所有者访问授权服务器的 /authorize 端点。请求通常通过浏览器重定向发起,必须包含以下参数:

  • response_type:固定值 code,表示请求授权码。
  • client_id:客户端在授权服务器注册时获得的唯一标识。
  • redirect_uri:授权成功后,授权服务器将用户重定向回客户端的地址。
  • scope:请求的权限范围(如 openid profile email)。
  • state:客户端生成的随机字符串,用于防止 CSRF 攻击。
安全要点:state 参数是授权码模式中防止跨站请求伪造(CSRF)的关键。客户端必须验证回调中的 state 值与发起请求时保存的值一致。
GET /authorize?
  response_type=code&
  client_id=web_app_001&
  redirect_uri=https://client.example.com/callback&
  scope=openid%20profile&
  state=xyzABC123
HTTP/1.1
Host: auth.example.com

2.3 阶段二:用户授权

授权服务器收到请求后,首先验证客户端的身份(通过 client_id)和 redirect_uri 的合法性。然后,如果用户尚未登录,授权服务器会展示登录页面;用户登录后,授权服务器会展示授权页面,告知用户客户端请求的权限范围。

  • 用户确认授权后,授权服务器生成一个临时的授权码。
  • 授权码与客户端标识、重定向 URI、授权范围绑定。
  • 授权码通常具有较短的有效期(如 5-10 分钟),且只能使用一次。
最佳实践:授权码应具有高熵值(至少 128 位随机数),且必须在服务端生成。禁止使用可预测的序列号或时间戳作为授权码。

2.4 阶段三:授权码发放

用户授权后,授权服务器通过浏览器将用户重定向回客户端指定的 redirect_uri,并在 URL 查询参数中附上授权码和原始的 state 值:

HTTP/1.1 302 Found
Location: https://client.example.com/callback?
  code=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...&
  state=xyzABC123

客户端收到回调后,必须立即验证 state 值是否与之前发送的一致。验证通过后,客户端提取授权码,准备进行令牌交换。

2.5 阶段四:令牌交换

客户端使用授权码,通过后端通道(服务器到服务器)向授权服务器的 /token 端点发起 POST 请求,换取访问令牌和可选的刷新令牌。此请求必须包含客户端认证信息。

POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)

grant_type=authorization_code&
code=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...&
redirect_uri=https://client.example.com/callback
参数 说明
grant_type 固定值 authorization_code
code 上一步获取的授权码
redirect_uri 必须与授权请求中的 redirect_uri 一致
client_id / client_secret 客户端凭证,用于认证客户端身份

授权服务器验证授权码、客户端身份和重定向 URI 后,返回令牌响应:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def50200a1b2c3d4e5f6...",
  "scope": "openid profile"
}

2.6 阶段五:刷新令牌

访问令牌过期后,客户端可以使用刷新令牌获取新的访问令牌,而无需用户再次授权。刷新令牌的请求同样通过后端通道发起:

POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)

grant_type=refresh_token&
refresh_token=def50200a1b2c3d4e5f6...&
scope=openid%20profile
安全警告:刷新令牌是长期凭证,必须安全存储。如果刷新令牌泄露,攻击者可以持续获取新的访问令牌。建议对刷新令牌实施轮换策略(每次使用后发放新的刷新令牌,并使旧的失效)。

2.7 完整序列图

下图展示了授权码模式的完整交互流程,包含所有五个阶段以及令牌刷新流程:

授权码模式完整序列图 授权码模式完整序列图 资源所有者 客户端 授权服务器 资源服务器 阶段一:授权码请求 1. 发起授权请求 (response_type=code, client_id, redirect_uri, scope, state) 2. 重定向到授权页面 3. 用户认证并授权 阶段二/三:授权与发放 4. 发放授权码 (code + state) 5. 重定向到客户端回调 (验证 state) 阶段四:令牌交换 6. POST /token (grant_type=authorization_code, code, client_secret) 7. 返回访问令牌 + 刷新令牌 8. 携带 access_token 请求资源 9. 返回受保护资源 阶段五:刷新令牌 令牌过期 11. POST /token (grant_type=refresh_token, refresh_token) 12. 返回新的访问令牌

2.8 关键安全校验点总结

阶段 校验点 风险
授权码请求 验证 redirect_uri 白名单 开放重定向漏洞
用户授权 验证 state 参数一致性 CSRF 攻击
令牌交换 验证授权码是否已使用、是否过期 授权码重用攻击
令牌交换 验证 client_secret 或客户端凭证 客户端身份伪造
刷新令牌 验证刷新令牌是否被吊销 令牌泄露持续访问
核心原则:授权码模式的安全性建立在“授权码一次性使用”、“后端通道令牌交换”、“state 防 CSRF”和“客户端认证”四重保障之上。任何一层的缺失都会导致安全漏洞。