授权码模式详解

授权码模式(Authorization Code Grant)是 OAuth 2.0 中最完整、最安全的授权流程,适用于有服务端后端的 Web 应用。该模式通过前端通道获取临时授权码,再通过后端安全通道交换访问令牌,确保令牌不会暴露在用户代理(浏览器)中。

授权码流程

授权码模式涉及四个参与方:资源所有者(用户)、用户代理(浏览器)、客户端(Web 应用后端)、授权服务器。完整流程包含以下步骤:

  1. 授权请求:客户端将资源所有者重定向到授权服务器的 /authorize 端点,附带客户端标识、重定向 URI、请求的 scope 和 response_type=code。
  2. 用户认证与授权:资源所有者在授权服务器上登录,并确认是否授权客户端访问其资源。
  3. 授权码下发:授权服务器通过重定向将授权码(authorization code)返回给客户端的回调地址。
  4. 令牌交换:客户端使用授权码,在后端通过 HTTPS 向授权服务器的 /token 端点发起 POST 请求,交换访问令牌和可选的刷新令牌。
  5. 受保护资源访问:客户端使用访问令牌向资源服务器请求用户资源。
关键安全属性:授权码是一次性、短时效(通常 1-10 分钟)的临时凭证,且必须通过客户端认证(client_secret 或私钥 JWT)才能交换令牌,防止授权码被截获后冒用。
授权码模式完整流程 授权码模式完整流程 资源所有者 用户代理 客户端 授权服务器 ① 授权请求 ② 用户认证与授权 ③ 授权码下发 ④ 令牌交换 ⑤ 受保护资源访问 授权码通过前端通道(浏览器)传递,令牌通过后端安全通道(服务器到服务器)交换,避免令牌暴露

授权码请求与响应

授权码请求分为两个阶段:授权请求(前端通道)和令牌请求(后端通道)。

授权请求(/authorize)

客户端将用户重定向到授权服务器的授权端点,请求参数如下:

参数 必需 说明
response_type 是 固定值 code
client_id 是 客户端标识符
redirect_uri 是 授权码回调地址,必须与注册时一致
scope 否 请求的权限范围,空格分隔
state 强烈推荐 防 CSRF 攻击的随机字符串,回调时原样返回
// 授权请求示例(浏览器重定向)
GET /authorize?
  response_type=code
  &client_id=web_app_001
  &redirect_uri=https://client.example.com/callback
  &scope=openid%20profile%20email
  &state=xyzABC123
  &nonce=abcDEF456
HTTP/1.1
Host: auth.example.com

授权码响应

用户授权后,授权服务器将用户重定向回客户端的回调地址,并在 URL 查询参数中附带授权码:

// 授权码响应示例(302 重定向)
HTTP/1.1 302 Found
Location: https://client.example.com/callback?
  code=authorization_code_abc123
  &state=xyzABC123
最佳实践:客户端必须验证 state 参数值与发起请求时生成的值一致,防止跨站请求伪造(CSRF)攻击。建议使用高熵随机字符串(如 128 位随机数)。

令牌请求(/token)

客户端在后端使用授权码向令牌端点发起 POST 请求,交换访问令牌:

参数 必需 说明
grant_type 是 固定值 authorization_code
code 是 上一步获取的授权码
redirect_uri 是 必须与授权请求中的 redirect_uri 一致
client_id 是 客户端标识符
client_secret 是 客户端密钥(机密客户端必需)
// 令牌请求示例(后端 POST)
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(web_app_001:client_secret_xyz)

grant_type=authorization_code
&code=authorization_code_abc123
&redirect_uri=https://client.example.com/callback

授权码交换令牌

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

// 令牌响应示例
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "refresh_token_abc789",
  "scope": "openid profile email",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}
响应字段 说明
access_token 访问令牌,用于调用资源服务器 API
token_type 令牌类型,通常为 Bearer
expires_in 访问令牌有效期(秒)
refresh_token 刷新令牌,用于获取新的访问令牌(可选)
scope 实际授予的权限范围
id_token OpenID Connect 扩展,包含用户身份信息
安全警告:授权码只能使用一次,且必须在获取后立即交换令牌。授权码有效期通常为 1-10 分钟,过期后必须重新发起授权流程。令牌交换请求必须通过 HTTPS 发送,防止中间人攻击。
授权码与令牌交换安全边界 授权码与令牌交换安全边界 前端通道(浏览器)— 不安全 后端通道(服务器)— 安全 资源所有者 用户代理 客户端后端 授权服务器 授权码 访问令牌 授权码暴露在浏览器中,但有效期短 且需要 client_secret 才能交换令牌

安全考虑

授权码模式虽然是最安全的 OAuth 2.0 流程,但仍需注意以下安全要点:

安全风险 描述 缓解措施
授权码截获 攻击者通过中间人攻击或浏览器历史记录获取授权码 强制使用 HTTPS;授权码一次性使用;短有效期(≤10 分钟)
CSRF 攻击 攻击者伪造授权回调,绑定用户账户 使用 state 参数并验证其值;使用 PKCE 扩展
授权码注入 攻击者将窃取的授权码注入到合法客户端的回调中 验证 redirect_uri 与注册时完全一致;绑定客户端身份
客户端凭证泄露 client_secret 被泄露导致令牌被冒领 使用私钥 JWT 客户端认证;定期轮换密钥;使用 PKCE
重定向 URI 篡改 攻击者修改回调地址将授权码发送到恶意服务器 授权服务器严格验证 redirect_uri 白名单
PKCE(Proof Key for Code Exchange)扩展:对于没有后端服务器的原生应用或单页应用,强烈建议使用 PKCE 扩展。客户端生成一个 code_verifier(随机字符串),并发送其 SHA-256 哈希值 code_challenge 到授权请求中。令牌交换时,客户端必须提供原始的 code_verifier,授权服务器验证哈希匹配。即使授权码被截获,没有 code_verifier 也无法交换令牌。

授权码模式安全清单

  • 始终使用 HTTPS:所有通信通道必须加密,防止中间人攻击。
  • 验证 state 参数:防止 CSRF 攻击,确保回调来自同一用户会话。
  • 严格验证 redirect_uri:授权服务器必须使用精确匹配,不允许通配符。
  • 授权码一次性使用:授权码使用后立即失效,重复使用应被拒绝并告警。
  • 短授权码有效期:建议 1-5 分钟,最长不超过 10 分钟。
  • 客户端认证:机密客户端必须使用 client_secret 或私钥 JWT 进行认证。
  • 使用 PKCE:即使对于有后端的 Web 应用,PKCE 也能提供额外保护层。
  • 最小权限原则:只请求应用实际需要的 scope,避免过度授权。
推荐配置:对于生产环境,建议同时启用 state 参数和 PKCE 扩展,即使授权码模式本身已经安全。这种纵深防御策略可以有效应对未来可能发现的新攻击向量。