3. 授权码流程完整时序:从用户点击登录到获取 Access Token 的完整 HTTP 请求/响应链

OAuth 2.0 授权码流程(Authorization Code Flow)是 Web 应用中最安全、最常用的授权模式。该流程涉及 资源所有者(用户)、用户代理(浏览器)、客户端(Web 应用)、授权服务器 和 资源服务器 五个参与方。理解完整的 HTTP 请求/响应链,是进行授权测试和边界验证的基础。

核心安全原则:授权码流程的关键在于 authorization_code 通过前端通道传递,而 access_token 仅通过后端安全通道(TLS)传输,确保令牌不被浏览器或第三方脚本截获。

3.1 完整时序步骤分解

从用户点击“使用第三方登录”到最终获取 access_token,共经历 6 个核心步骤,形成两条独立的通信通道:前端通道(浏览器 ↔ 授权服务器)和 后端通道(客户端 ↔ 授权服务器)。

步骤 发起方 接收方 通道类型 核心操作
1 用户(浏览器) 客户端(Web 应用) 前端 点击“登录”按钮,触发授权请求
2 客户端(302 重定向) 授权服务器 前端 构造授权请求 URL,重定向用户浏览器
3 用户(浏览器) 授权服务器 前端 用户认证并同意授权(输入凭据、确认 scope)
4 授权服务器(302 重定向) 客户端(回调端点) 前端 返回 authorization_code 到回调 URL
5 客户端(POST 请求) 授权服务器(令牌端点) 后端 用 authorization_code 换取 access_token
6 授权服务器 客户端 后端 返回 access_token + refresh_token(可选)
测试要点:步骤 2 和步骤 4 使用 HTTP 302 重定向,属于前端通道;步骤 5 和步骤 6 使用直接 POST/JSON 响应,属于后端通道。测试时应分别验证两条通道的完整性、参数校验和错误处理。

3.2 授权码流程完整时序图

下图展示了从用户点击登录到获取 Access Token 的完整 HTTP 请求/响应链,包含前端通道和后端通道的交互细节:

OAuth 2.0 授权码流程完整时序图 OAuth 2.0 授权码流程完整时序图 用户 (浏览器) 客户端 (Web 应用) 授权服务器 资源服务器 ① 点击“登录”按钮 ② 构造授权请求 URL 302 重定向 ③ 用户认证 + 授权 ④ 302 回调 + code ⑤ POST 换取 token ⑥ 返回 access_token ⑦ 使用 token 请求资源 ⑧ 返回受保护资源 前端通道 后端通道

3.3 关键 HTTP 请求/响应详解

3.3.1 授权请求(步骤 2)

客户端构造授权请求 URL,通过 HTTP 302 重定向将用户浏览器导向授权服务器的 /authorize 端点:

GET /authorize?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://client.example.com/callback
  &scope=openid%20profile%20email
  &state=xyzABC123
  &nonce=abcDEF456
HTTP/1.1
Host: auth-server.example.com
参数 必需 说明 测试关注点
response_type 是 固定值 code,标识授权码流程 篡改为 token 是否被拒绝
client_id 是 客户端标识符,由授权服务器预注册 使用未注册的 client_id 是否返回错误
redirect_uri 是 授权码回调地址,必须与预注册一致 开放重定向漏洞、host 篡改
scope 是 请求的权限范围,空格分隔 越权 scope 是否被限制
state 强烈推荐 CSRF 令牌,防止跨站请求伪造 缺失 state 是否被拒绝;state 可预测性
nonce OpenID 场景 防止重放攻击的一次性随机值 nonce 重用是否被检测

3.3.2 授权码回调(步骤 4)

用户认证并授权后,授权服务器通过 302 重定向将 authorization_code 和 state 返回给客户端的回调端点:

HTTP/1.1 302 Found
Location: https://client.example.com/callback?code=AUTH_CODE_12345&state=xyzABC123
安全测试重点:授权码 code 是一次性的,有效期通常为 1-10 分钟。测试时应验证:
(1) 重复使用同一授权码是否被拒绝;
(2) 授权码是否绑定到特定的 redirect_uri 和 client_id;
(3) state 参数是否与发起请求时一致,防止 CSRF 攻击。

3.3.3 令牌请求(步骤 5)

客户端通过后端通道(服务器到服务器)向授权服务器的 /token 端点发送 POST 请求,使用 authorization_code 换取 access_token:

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

grant_type=authorization_code
&code=AUTH_CODE_12345
&redirect_uri=https://client.example.com/callback
&client_id=YOUR_CLIENT_ID
参数 必需 说明 测试关注点
grant_type 是 固定值 authorization_code 篡改为其他 grant_type 是否被拒绝
code 是 上一步获取的授权码 过期、伪造、已使用的 code 处理
redirect_uri 是 必须与授权请求中的 redirect_uri 一致 不一致时是否返回错误
client_id 是 客户端标识符 client_id 与 code 是否绑定
client_secret 是 客户端密钥,通过 Basic Auth 或 POST body 传递 密钥泄露、弱密钥、空密钥测试

3.3.4 令牌响应(步骤 6)

授权服务器验证通过后,返回 access_token 和可选的 refresh_token:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "REFRESH_TOKEN_67890",
  "scope": "openid profile email",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}
边界验证要点:
  • 令牌格式:验证 access_token 是否为 JWT 格式,检查签名算法(推荐 RS256 而非 HS256)
  • 过期时间:expires_in 是否在合理范围(通常 3600 秒),是否存在永不过期的令牌
  • scope 一致性:返回的 scope 是否与授权请求一致,是否存在 scope 提升漏洞
  • refresh_token:是否支持轮换(Rotation),旧 refresh_token 被使用后是否失效
  • 响应头:Cache-Control: no-store 和 Pragma: no-cache 是否强制禁止缓存

3.4 错误处理与边界场景

授权码流程中,授权服务器在遇到错误时必须返回标准的错误响应。测试时应验证以下错误场景:

错误码 触发条件 HTTP 状态码 测试方法
invalid_request 缺少必需参数或参数格式错误 400 删除 response_type 或 client_id
unauthorized_client 客户端未授权使用该 grant_type 401 使用未注册的 client_id
access_denied 用户拒绝授权 302 + error 参数 模拟用户点击“拒绝”
invalid_grant 授权码无效、过期或已被使用 400 重复使用同一授权码
redirect_uri_mismatch 回调 URL 与预注册不一致 400 篡改 redirect_uri 参数
invalid_scope 请求的 scope 无效或超出授权范围 400 请求未注册的 scope 值
Go 测试实践:使用 net/http/httptest 模拟授权服务器,编写表格驱动测试覆盖所有错误场景。重点验证授权码的 一次性、绑定性(绑定到 client_id + redirect_uri)和 时效性。

3.5 安全测试清单

基于完整时序链,总结以下安全测试要点:

  1. CSRF 防护:验证 state 参数是否存在、是否随机、是否与服务端 session 绑定
  2. 开放重定向:测试 redirect_uri 是否允许任意域名、路径穿越、协议混淆(http:// vs https://)
  3. 授权码泄露:验证授权码是否出现在日志、Referer 头、浏览器历史中
  4. 授权码重用:同一授权码多次请求令牌端点是否返回 invalid_grant
  5. 令牌劫持:验证 access_token 是否仅通过后端通道传输,前端通道不得暴露令牌
  6. scope 提升:在令牌请求阶段修改 scope 参数是否被拒绝
  7. client_secret 强度:测试空密钥、弱密钥、硬编码密钥场景
  8. 令牌存储:验证服务端是否以安全方式存储令牌(加密、不记录日志)