授权码模式详解
授权码模式(Authorization Code Grant)是 OAuth 2.0 中最完整、最安全的授权流程,适用于有服务端后端的 Web 应用。该模式通过前端通道获取临时授权码,再通过后端安全通道交换访问令牌,确保令牌不会暴露在用户代理(浏览器)中。
授权码流程
授权码模式涉及四个参与方:资源所有者(用户)、用户代理(浏览器)、客户端(Web 应用后端)、授权服务器。完整流程包含以下步骤:
- 授权请求:客户端将资源所有者重定向到授权服务器的 /authorize 端点,附带客户端标识、重定向 URI、请求的 scope 和 response_type=code。
- 用户认证与授权:资源所有者在授权服务器上登录,并确认是否授权客户端访问其资源。
- 授权码下发:授权服务器通过重定向将授权码(authorization code)返回给客户端的回调地址。
- 令牌交换:客户端使用授权码,在后端通过 HTTPS 向授权服务器的 /token 端点发起 POST 请求,交换访问令牌和可选的刷新令牌。
- 受保护资源访问:客户端使用访问令牌向资源服务器请求用户资源。
关键安全属性:授权码是一次性、短时效(通常 1-10 分钟)的临时凭证,且必须通过客户端认证(client_secret 或私钥 JWT)才能交换令牌,防止授权码被截获后冒用。
授权码请求与响应
授权码请求分为两个阶段:授权请求(前端通道)和令牌请求(后端通道)。
授权请求(/authorize)
客户端将用户重定向到授权服务器的授权端点,请求参数如下:
| 参数 | 必需 | 说明 |
|---|---|---|
response_type |
是 | 固定值 code |
client_id |
是 | 客户端标识符 |
redirect_uri |
是 | 授权码回调地址,必须与注册时一致 |
scope |
否 | 请求的权限范围,空格分隔 |
state |
强烈推荐 | 防 CSRF 攻击的随机字符串,回调时原样返回 |
// 授权请求示例(浏览器重定向)
GET /authorize?
response_type=code
&client_id=web_app_001
&redirect_uri=https://client.example.com/callback
&scope=openid%20profile%20email
&state=xyzABC123
&nonce=abcDEF456
HTTP/1.1
Host: auth.example.com
授权码响应
用户授权后,授权服务器将用户重定向回客户端的回调地址,并在 URL 查询参数中附带授权码:
// 授权码响应示例(302 重定向)
HTTP/1.1 302 Found
Location: https://client.example.com/callback?
code=authorization_code_abc123
&state=xyzABC123
最佳实践:客户端必须验证
state 参数值与发起请求时生成的值一致,防止跨站请求伪造(CSRF)攻击。建议使用高熵随机字符串(如 128 位随机数)。
令牌请求(/token)
客户端在后端使用授权码向令牌端点发起 POST 请求,交换访问令牌:
| 参数 | 必需 | 说明 |
|---|---|---|
grant_type |
是 | 固定值 authorization_code |
code |
是 | 上一步获取的授权码 |
redirect_uri |
是 | 必须与授权请求中的 redirect_uri 一致 |
client_id |
是 | 客户端标识符 |
client_secret |
是 | 客户端密钥(机密客户端必需) |
// 令牌请求示例(后端 POST)
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(web_app_001:client_secret_xyz)
grant_type=authorization_code
&code=authorization_code_abc123
&redirect_uri=https://client.example.com/callback
授权码交换令牌
授权服务器验证授权码和客户端身份后,返回令牌响应:
// 令牌响应示例
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "refresh_token_abc789",
"scope": "openid profile email",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}
| 响应字段 | 说明 |
|---|---|
access_token |
访问令牌,用于调用资源服务器 API |
token_type |
令牌类型,通常为 Bearer |
expires_in |
访问令牌有效期(秒) |
refresh_token |
刷新令牌,用于获取新的访问令牌(可选) |
scope |
实际授予的权限范围 |
id_token |
OpenID Connect 扩展,包含用户身份信息 |
安全警告:授权码只能使用一次,且必须在获取后立即交换令牌。授权码有效期通常为 1-10 分钟,过期后必须重新发起授权流程。令牌交换请求必须通过 HTTPS 发送,防止中间人攻击。
安全考虑
授权码模式虽然是最安全的 OAuth 2.0 流程,但仍需注意以下安全要点:
| 安全风险 | 描述 | 缓解措施 |
|---|---|---|
| 授权码截获 | 攻击者通过中间人攻击或浏览器历史记录获取授权码 | 强制使用 HTTPS;授权码一次性使用;短有效期(≤10 分钟) |
| CSRF 攻击 | 攻击者伪造授权回调,绑定用户账户 | 使用 state 参数并验证其值;使用 PKCE 扩展 |
| 授权码注入 | 攻击者将窃取的授权码注入到合法客户端的回调中 | 验证 redirect_uri 与注册时完全一致;绑定客户端身份 |
| 客户端凭证泄露 | client_secret 被泄露导致令牌被冒领 |
使用私钥 JWT 客户端认证;定期轮换密钥;使用 PKCE |
| 重定向 URI 篡改 | 攻击者修改回调地址将授权码发送到恶意服务器 | 授权服务器严格验证 redirect_uri 白名单 |
PKCE(Proof Key for Code Exchange)扩展:对于没有后端服务器的原生应用或单页应用,强烈建议使用 PKCE 扩展。客户端生成一个
code_verifier(随机字符串),并发送其 SHA-256 哈希值 code_challenge 到授权请求中。令牌交换时,客户端必须提供原始的 code_verifier,授权服务器验证哈希匹配。即使授权码被截获,没有 code_verifier 也无法交换令牌。
授权码模式安全清单
- 始终使用 HTTPS:所有通信通道必须加密,防止中间人攻击。
- 验证 state 参数:防止 CSRF 攻击,确保回调来自同一用户会话。
- 严格验证 redirect_uri:授权服务器必须使用精确匹配,不允许通配符。
- 授权码一次性使用:授权码使用后立即失效,重复使用应被拒绝并告警。
- 短授权码有效期:建议 1-5 分钟,最长不超过 10 分钟。
- 客户端认证:机密客户端必须使用 client_secret 或私钥 JWT 进行认证。
- 使用 PKCE:即使对于有后端的 Web 应用,PKCE 也能提供额外保护层。
- 最小权限原则:只请求应用实际需要的 scope,避免过度授权。
推荐配置:对于生产环境,建议同时启用
state 参数和 PKCE 扩展,即使授权码模式本身已经安全。这种纵深防御策略可以有效应对未来可能发现的新攻击向量。