2. 授权码模式详解:授权码请求、用户授权、授权码发放、令牌交换、访问令牌与刷新令牌的获取流程

授权码模式(Authorization Code Grant)是 OAuth 2.0 中最完整、最安全的授权流程,适用于有服务端后端的 Web 应用。其核心在于通过一个临时的“授权码”作为中间凭证,避免访问令牌直接暴露在用户代理(浏览器)中。本节将拆解该模式的五个核心阶段,并配合流程图与关键参数表格进行深度解析。

2.1 授权码模式完整流程概览

整个流程涉及四个角色:资源所有者(用户)、用户代理(浏览器)、客户端(第三方应用)、授权服务器。以下 SVG 图展示了从授权请求发起至令牌获取的完整闭环。

授权码模式完整流程 授权码模式完整流程(Authorization Code Grant) 资源所有者 用户代理 客户端 授权服务器 ① 发起授权请求 构造授权请求 URL ② 302 重定向 ③ 携带参数请求 ④ 授权请求到达 用户认证页面 ⑤ 输入凭证 ⑥ 授权确认 ⑦ 提交授权 生成授权码 ⑧ 授权码回调 ⑨ 携带授权码 ⑩ 回调客户端 服务端令牌请求 ⑪ POST 令牌端点 验证授权码 ⑫ 返回令牌 存储令牌

2.2 阶段一:授权码请求(Authorization Request)

客户端引导资源所有者访问授权服务器的 /authorize 端点。此请求通过浏览器重定向完成,关键参数如下:

参数 是否必需 说明
response_type 是 固定值 code,表示使用授权码模式
client_id 是 客户端在授权服务器注册时获得的唯一标识
redirect_uri 是 授权码发送回客户端的地址,必须与注册时一致
scope 否 请求的权限范围,如 openid profile email
state 强烈推荐 防 CSRF 攻击的随机字符串,回调时原样返回
安全要点: state 参数是防止跨站请求伪造(CSRF)的关键。客户端必须生成并验证该参数,确保回调请求来自同一用户会话。

2.3 阶段二:用户认证与授权(User Authentication & Consent)

授权服务器收到请求后,执行以下步骤:

  1. 验证客户端身份:检查 client_id 是否有效,redirect_uri 是否匹配白名单。
  2. 用户认证:如果用户未登录,展示登录页面,要求输入凭证(用户名/密码、MFA 等)。
  3. 展示授权页面:向用户展示客户端请求的权限范围(如“读取您的个人信息”),用户点击“允许”或“拒绝”。
  4. 记录用户决策:授权服务器记录用户授权结果,生成临时授权码。
最佳实践: 授权页面应清晰列出所有请求的 scope,并提供“记住此授权”选项,减少重复授权操作。

2.4 阶段三:授权码发放(Authorization Code Issuance)

用户授权后,授权服务器生成一个短生命周期的授权码,并通过重定向将授权码发送回客户端指定的 redirect_uri。回调 URL 格式如下:

https://client.example.com/callback?code=AUTHORIZATION_CODE&state=XYZ123

授权码具有以下关键属性:

  • 一次性使用:使用后立即失效,防止重放攻击。
  • 短生命周期:通常有效期为 1~10 分钟,降低泄露风险。
  • 绑定客户端:授权码与特定 client_id 和 redirect_uri 绑定。
安全警告: 授权码必须通过 TLS 传输。如果授权码在传输过程中被截获,攻击者可能利用它换取令牌。务必使用 HTTPS 并验证 state 参数。

2.5 阶段四:令牌交换(Token Exchange)

客户端使用授权码向授权服务器的 /token 端点发起 POST 请求,换取访问令牌和刷新令牌。此步骤在客户端服务端进行,不经过浏览器,安全性更高。

令牌交换请求参数:

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

grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=https://client.example.com/callback
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
参数 是否必需 说明
grant_type 是 固定值 authorization_code
code 是 上一步获取的授权码
redirect_uri 是 必须与授权请求中的 redirect_uri 一致
client_id 是 客户端标识
client_secret 是 客户端密钥,用于验证客户端身份

2.6 阶段五:访问令牌与刷新令牌获取(Access & Refresh Token)

授权服务器验证授权码和客户端凭证后,返回令牌响应。典型响应如下:

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

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

令牌字段说明:

字段 说明
access_token 访问令牌,用于访问受保护资源,通常为 JWT 格式
token_type 令牌类型,固定为 Bearer
expires_in 访问令牌有效期(秒),通常为 3600(1 小时)
refresh_token 刷新令牌,用于在访问令牌过期后获取新的访问令牌
scope 实际授予的权限范围,可能与请求的 scope 不同

2.7 刷新令牌流程(Refresh Token Flow)

当访问令牌过期时,客户端可以使用刷新令牌获取新的访问令牌,无需用户重新授权。刷新请求如下:

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

grant_type=refresh_token
&refresh_token=REFRESH_TOKEN
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET

刷新令牌的注意事项:

  • 长期有效:刷新令牌有效期通常较长(数天至数月),但可被撤销。
  • 轮换策略:每次使用刷新令牌后,授权服务器可返回新的刷新令牌,旧令牌失效。
  • 安全存储:刷新令牌必须安全存储在服务端,避免泄露。
关键对比: 访问令牌用于访问资源,生命周期短;刷新令牌用于获取新的访问令牌,生命周期长。两者必须分开存储,刷新令牌不应暴露给前端。

2.8 授权码模式安全断言总结

授权码模式通过以下机制保障安全:

  • 授权码隔离:访问令牌不经过浏览器,降低令牌泄露风险。
  • 一次性授权码:防止重放攻击。
  • 客户端认证:令牌交换阶段要求 client_secret,确保只有合法客户端能换取令牌。
  • state 参数:防御 CSRF 攻击。
  • 刷新令牌轮换:减少长期令牌泄露的影响。