2. OAuth2.0 授权码模式(Authorization Code)
授权码模式是 OAuth2.0 中最完整、最安全的授权流程,适用于有服务端后端的 Web 应用和移动应用。其核心思想是:授权码作为临时中间凭证,在客户端与授权服务器之间传递,最终由后端服务器换取访问令牌,从而避免访问令牌直接暴露在用户代理(浏览器)中。
2.1 授权码流程详解
授权码模式涉及四个核心角色:资源所有者(用户)、客户端(第三方应用)、授权服务器、资源服务器。完整流程包含以下六个步骤:
- 客户端发起授权请求:客户端引导用户跳转到授权服务器的授权页面,携带
response_type=code、client_id、redirect_uri、scope等参数。 - 用户认证并授权:用户在授权服务器上输入凭证,确认授权范围。
- 授权服务器返回授权码:授权服务器通过重定向将授权码(code)附加在回调 URL 上,返回给客户端。
- 客户端换取访问令牌:客户端后端使用授权码、
client_secret等参数,向授权服务器的令牌端点发起 POST 请求。 - 授权服务器返回访问令牌:验证通过后,授权服务器返回
access_token、refresh_token、expires_in等。 - 客户端访问资源服务器:客户端使用访问令牌请求受保护的 API 资源。
关键安全设计:授权码是一次性的,有效期通常为 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"
}
刷新令牌策略:授权服务器可以返回新的刷新令牌(轮换策略),也可以返回相同的刷新令牌(非轮换策略)。轮换策略安全性更高,每次刷新后旧刷新令牌立即失效,可有效防止刷新令牌泄露后的长期滥用。
2.5 授权码模式安全要点
- 授权码一次性使用:授权服务器必须确保每个授权码只能使用一次,使用后立即标记为已用。
- PKCE 扩展:对于没有服务端后端的应用(如纯前端 SPA、移动 App),建议使用 PKCE(Proof Key for Code Exchange)增强安全性,防止授权码被截获后直接使用。
- HTTPS 强制:所有与授权服务器和资源服务器的通信必须使用 HTTPS,防止中间人攻击。
- 最小权限原则:只请求应用实际需要的
scope,避免过度授权。 - 令牌存储:访问令牌和刷新令牌在客户端后端应安全存储,避免泄露到日志、错误信息或前端代码中。
实战建议:在实现授权码模式时,建议使用成熟的 OAuth2.0 客户端库(如 Spring Security OAuth2、Passport.js、oauthlib),避免自行实现协议细节。同时,务必在开发环境开启详细的日志记录,便于调试授权流程中的问题。