2. OAuth2.0 授权码模式(Authorization Code)

授权码模式是 OAuth2.0 中最完整、最安全的授权流程,适用于有服务端后端的 Web 应用和移动应用。其核心思想是:授权码作为临时中间凭证,在客户端与授权服务器之间传递,最终由后端服务器换取访问令牌,从而避免访问令牌直接暴露在用户代理(浏览器)中。

2.1 授权码流程详解

授权码模式涉及四个核心角色:资源所有者(用户)、客户端(第三方应用)、授权服务器、资源服务器。完整流程包含以下六个步骤:

  1. 客户端发起授权请求:客户端引导用户跳转到授权服务器的授权页面,携带 response_type=code、client_id、redirect_uri、scope 等参数。
  2. 用户认证并授权:用户在授权服务器上输入凭证,确认授权范围。
  3. 授权服务器返回授权码:授权服务器通过重定向将授权码(code)附加在回调 URL 上,返回给客户端。
  4. 客户端换取访问令牌:客户端后端使用授权码、client_secret 等参数,向授权服务器的令牌端点发起 POST 请求。
  5. 授权服务器返回访问令牌:验证通过后,授权服务器返回 access_token、refresh_token、expires_in 等。
  6. 客户端访问资源服务器:客户端使用访问令牌请求受保护的 API 资源。
关键安全设计:授权码是一次性的,有效期通常为 1-10 分钟;即使被截获,也无法直接用于访问资源,必须配合 client_secret 才能换取令牌。
授权码模式完整流程 授权码模式完整流程 用户(资源所有者) 客户端(第三方应用) 授权服务器 资源服务器 ① 发起授权请求 ② 请求授权码 ③ 用户认证并授权 ④ 返回授权码(code) ⑤ 用授权码换取令牌 ⑥ 返回 access_token ⑦ 携带令牌访问资源 ⑧ 返回受保护资源 说明:授权码(code)通过前端重定向传递,访问令牌(access_token)通过后端直接通信传递,避免令牌暴露在浏览器中。 授权码一次性有效,有效期通常为 1-10 分钟,配合 client_secret 才能换取令牌。

2.2 获取授权码

客户端需要将用户重定向到授权服务器的 /authorize 端点,携带以下必要参数:

参数 是否必需 说明 示例值
response_type 是 固定值 code,表示使用授权码模式 code
client_id 是 客户端在授权服务器注册时获得的唯一标识 app_123456
redirect_uri 是 授权码回调地址,必须与注册时一致 https://myapp.com/callback
scope 否 请求的权限范围,多个用空格分隔 read write
state 强烈推荐 防 CSRF 攻击的随机字符串,回调时原样返回 xyz789

授权请求示例:

GET /authorize?
  response_type=code
  &client_id=app_123456
  &redirect_uri=https%3A%2F%2Fmyapp.com%2Fcallback
  &scope=read+write
  &state=xyz789
HTTP/1.1
Host: auth-server.com

用户认证并授权后,授权服务器通过 302 重定向将授权码返回给客户端:

HTTP/1.1 302 Found
Location: https://myapp.com/callback?
  code=AUTHORIZATION_CODE_123
  &state=xyz789
最佳实践:务必验证回调中的 state 参数与发起请求时生成的 state 一致,防止 CSRF 攻击。建议使用 UUID 或加密随机数生成 state,并在服务端会话中存储。

2.3 换取访问令牌

客户端后端收到授权码后,立即向授权服务器的 /token 端点发起 POST 请求,携带以下参数:

参数 是否必需 说明 示例值
grant_type 是 固定值 authorization_code authorization_code
code 是 上一步获取的授权码 AUTHORIZATION_CODE_123
redirect_uri 是 必须与授权请求中的 redirect_uri 一致 https://myapp.com/callback
client_id 是 客户端标识 app_123456
client_secret 是 客户端密钥,仅在后端使用 secret_abc

令牌请求示例:

POST /token HTTP/1.1
Host: auth-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTHORIZATION_CODE_123
&redirect_uri=https%3A%2F%2Fmyapp.com%2Fcallback
&client_id=app_123456
&client_secret=secret_abc

授权服务器验证通过后,返回 JSON 格式的令牌响应:

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

{
  "access_token": "ACCESS_TOKEN_ABC",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "REFRESH_TOKEN_XYZ",
  "scope": "read write"
}
安全警告:令牌请求必须在客户端后端服务器发起,绝不能在浏览器前端 JavaScript 中直接请求,否则 client_secret 会暴露给用户。授权码使用后应立即失效,防止重放攻击。

2.4 刷新令牌

访问令牌通常有较短的有效期(如 1 小时),过期后客户端可以使用刷新令牌获取新的访问令牌,无需用户重新授权。刷新令牌的有效期通常较长(如 30 天),且可以多次使用。

刷新令牌请求参数:

参数 是否必需 说明 示例值
grant_type 是 固定值 refresh_token refresh_token
refresh_token 是 换取访问令牌时获得的刷新令牌 REFRESH_TOKEN_XYZ
client_id 是 客户端标识 app_123456
client_secret 是 客户端密钥 secret_abc

刷新令牌请求示例:

POST /token HTTP/1.1
Host: auth-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=REFRESH_TOKEN_XYZ
&client_id=app_123456
&client_secret=secret_abc

刷新令牌响应示例:

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

{
  "access_token": "NEW_ACCESS_TOKEN_456",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "NEW_REFRESH_TOKEN_789",
  "scope": "read write"
}
刷新令牌策略:授权服务器可以返回新的刷新令牌(轮换策略),也可以返回相同的刷新令牌(非轮换策略)。轮换策略安全性更高,每次刷新后旧刷新令牌立即失效,可有效防止刷新令牌泄露后的长期滥用。
令牌生命周期与刷新流程 令牌生命周期与刷新流程 获取访问令牌 使用令牌访问资源 令牌过期(401) 使用刷新令牌 获取新的访问令牌,继续使用 刷新令牌生命周期管理 • 访问令牌(access_token):短期有效(通常 1 小时),用于直接访问资源 • 刷新令牌(refresh_token):长期有效(通常 30 天),用于获取新的访问令牌 • 轮换策略:每次刷新后返回新的刷新令牌,旧令牌立即失效

2.5 授权码模式安全要点

  • 授权码一次性使用:授权服务器必须确保每个授权码只能使用一次,使用后立即标记为已用。
  • PKCE 扩展:对于没有服务端后端的应用(如纯前端 SPA、移动 App),建议使用 PKCE(Proof Key for Code Exchange)增强安全性,防止授权码被截获后直接使用。
  • HTTPS 强制:所有与授权服务器和资源服务器的通信必须使用 HTTPS,防止中间人攻击。
  • 最小权限原则:只请求应用实际需要的 scope,避免过度授权。
  • 令牌存储:访问令牌和刷新令牌在客户端后端应安全存储,避免泄露到日志、错误信息或前端代码中。
实战建议:在实现授权码模式时,建议使用成熟的 OAuth2.0 客户端库(如 Spring Security OAuth2、Passport.js、oauthlib),避免自行实现协议细节。同时,务必在开发环境开启详细的日志记录,便于调试授权流程中的问题。