3. 授权码模式完整流程:用户授权、获取授权码、换取访问令牌、携带令牌访问资源、令牌刷新

授权码模式(Authorization Code Grant)是 OAuth 2.0 中最安全、应用最广泛的授权流程,特别适用于有后端服务器的 Web 应用。其核心思想是:授权码作为临时凭证在浏览器端传递,而访问令牌则通过安全的后端通道交换,避免令牌暴露在用户代理(浏览器)中。

完整流程包含五个关键步骤,每一步都涉及特定的端点、参数和安全约束。下面先通过一张整体流程图建立全局认知,再逐一拆解每个环节。

授权码模式完整流程 授权码模式完整流程(5步) 资源所有者 (用户) 用户代理 (浏览器) 客户端 (后端服务) 授权服务器 (AS) 资源服务器 (RS) ① 访问资源 ② 发起授权请求 ③ 请求授权码 ④ 返回授权码(302重定向) ⑤ 携带授权码回调 ⑥ 换取令牌(POST) ⑦ 返回访问令牌+刷新令牌 ⑧ 携带令牌请求资源 ⑨ 返回资源 令牌刷新(可选) 客户端 → 授权服务器 刷新令牌流程

3.1 步骤一:用户授权(User Authorization)

流程的起点是用户试图访问客户端应用上的受保护资源。客户端检测到用户未登录或缺少有效令牌时,将用户引导至授权服务器的授权端点。

关键动作:客户端构造授权请求 URL,通过 302 重定向将用户浏览器发送到授权服务器。
// 授权请求 URL 示例
GET /authorize?
  response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://your-app.com/callback
  &scope=openid%20profile%20email
  &state=xyzABC123
HTTP/1.1
Host: authorization-server.com
参数必填说明
response_type是固定值 code,表示使用授权码模式
client_id是客户端在授权服务器注册时获得的标识
redirect_uri是授权成功后回调地址,必须与注册时一致
scope否请求的权限范围,空格分隔
state强烈推荐防 CSRF 攻击的随机字符串,回调时原样返回
安全要点:state 参数是防止跨站请求伪造(CSRF)的关键手段。客户端必须生成不可预测的随机值,并在回调时严格校验。缺少 state 校验是生产环境中最常见的安全漏洞之一。

3.2 步骤二:获取授权码(Authorization Code)

用户到达授权服务器后,需要完成身份认证(登录)并授权客户端请求的权限。授权服务器验证用户身份后,生成一个临时的授权码,通过重定向将授权码发送回客户端指定的 redirect_uri。

// 授权成功后的回调 URL 示例
HTTP/1.1 302 Found
Location: https://your-app.com/callback?
  code=AUTHORIZATION_CODE_12345
  &state=xyzABC123
授权码特性:授权码是短生命期的临时凭证(通常 1-10 分钟),且只能使用一次。即使被截获,攻击者也难以在短时间内完成令牌交换,且无法重复使用。

3.3 步骤三:换取访问令牌(Access Token)

客户端收到授权码后,通过后端通道(服务器到服务器)向授权服务器的令牌端点发起 POST 请求,换取访问令牌。这一步是授权码模式的安全核心——令牌交换不经过浏览器,避免了令牌泄露。

// 令牌交换请求
POST /token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)

grant_type=authorization_code
&code=AUTHORIZATION_CODE_12345
&redirect_uri=https://your-app.com/callback
// 令牌响应示例
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "REFRESH_TOKEN_67890",
  "scope": "openid profile email"
}
请求参数必填说明
grant_type是固定值 authorization_code
code是上一步获取的授权码
redirect_uri是必须与授权请求中的值一致
client_id / client_secret是客户端身份认证,可通过 Basic Auth 或 POST 参数传递
安全关键:客户端凭证(client_secret)绝不能暴露在浏览器端。令牌交换请求必须从后端服务器发起,这是授权码模式区别于隐式模式的核心安全优势。

3.4 步骤四:携带令牌访问资源(Access Resource)

客户端获得访问令牌后,在请求受保护资源时,将令牌放入 HTTP 请求的 Authorization 头中,以 Bearer 令牌的形式发送给资源服务器。

// 携带令牌访问资源
GET /api/user/profile HTTP/1.1
Host: resource-server.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

资源服务器验证令牌的有效性(包括签名、过期时间、作用域等),如果验证通过,返回请求的资源数据;否则返回 401 Unauthorized 错误。

令牌验证方式:资源服务器可以通过两种方式验证令牌:
1. 自省(Introspection):将令牌发送给授权服务器验证,适用于不透明令牌。
2. 本地验证:如果使用 JWT 格式的令牌,资源服务器可自行验证签名和声明,无需额外网络请求。

3.5 步骤五:令牌刷新(Token Refresh)

访问令牌通常有较短的有效期(如 1 小时)。当令牌过期时,客户端可以使用刷新令牌(Refresh Token)向授权服务器请求新的访问令牌,而无需用户重新授权。

// 令牌刷新请求
POST /token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)

grant_type=refresh_token
&refresh_token=REFRESH_TOKEN_67890
&scope=openid%20profile
// 刷新响应示例
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
  "access_token": "NEW_ACCESS_TOKEN_ABC...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "NEW_REFRESH_TOKEN_DEF...",
  "scope": "openid profile"
}
令牌刷新流程 令牌刷新流程 客户端 (后端服务) 授权服务器 (AS) ① 访问令牌过期(401) ② 发送刷新令牌 ③ 返回新访问令牌 刷新令牌是长期凭证(通常数天到数月),必须安全存储 授权服务器可选择性返回新的刷新令牌(轮换策略)
刷新令牌安全策略:
  • 存储安全:刷新令牌必须安全存储在服务端,绝不能暴露给浏览器或前端代码。
  • 轮换策略:每次刷新时,授权服务器可以返回新的刷新令牌并使旧令牌失效,防止令牌泄露后的长期滥用。
  • 撤销机制:授权服务器应提供刷新令牌的撤销端点,当用户主动注销或检测到异常时,客户端应主动撤销刷新令牌。

3.6 流程总结与关键对比

步骤参与者通信通道关键凭证安全风险
用户授权用户 → 浏览器 → 授权服务器前端(浏览器重定向)无(用户认证)钓鱼、CSRF(state 防护)
获取授权码授权服务器 → 浏览器 → 客户端前端(302 重定向)授权码(短生命期)授权码截获(短生命期+一次性)
换取访问令牌客户端 → 授权服务器后端(服务器到服务器)授权码 + client_secret凭证泄露(TLS 加密)
访问资源客户端 → 资源服务器后端访问令牌令牌泄露(Bearer 令牌)
令牌刷新客户端 → 授权服务器后端刷新令牌刷新令牌泄露(轮换+撤销)
最佳实践总结:
  • 始终使用 state 参数并严格校验,防止 CSRF 攻击。
  • 授权码仅能使用一次,且有效期不超过 10 分钟。
  • 令牌交换必须通过后端服务器完成,client_secret 绝不暴露。
  • 访问令牌使用 Bearer 方案,通过 Authorization 头传递。
  • 刷新令牌实施轮换策略,并支持主动撤销。
  • 所有通信必须使用 TLS/HTTPS 加密。