2. 授权码流程详解:授权请求、用户认证、授权码发放、令牌交换、令牌刷新、令牌撤销的完整生命周期
OIDC(OpenID Connect)授权码流程(Authorization Code Flow)是当前最安全、最常用的身份认证与授权协议之一。它通过前端通道与后端通道的分离,确保敏感令牌(如 Refresh Token)不会暴露在浏览器或移动端环境中。本章将拆解该流程的六个核心阶段,并分析每个阶段在异步任务编排中的关键优化点。
2.1 流程全景图
下图展示了授权码流程的完整生命周期,包含客户端、授权服务器、资源服务器以及用户代理之间的交互顺序。
2.2 阶段一:授权请求(Authorization Request)
客户端构造授权请求 URL,将用户代理重定向至授权服务器的 /authorize 端点。此阶段的关键参数如下:
| 参数 | 必选 | 说明 |
|---|---|---|
response_type |
是 | 固定为 code,表示使用授权码流程 |
client_id |
是 | 客户端在授权服务器注册时获得的标识 |
redirect_uri |
是 | 授权码回调地址,必须与注册时一致 |
scope |
是 | 请求的权限范围,如 openid profile email |
state |
强烈推荐 | 防 CSRF 攻击的随机字符串,回调时需校验 |
nonce |
推荐 | 防重放攻击的随机值,会嵌入 ID Token |
state 参数作为回调任务的关联 ID,便于在回调到达时快速匹配对应的异步上下文。
2.3 阶段二:用户认证(User Authentication)
用户代理到达授权服务器后,服务器根据用户当前的会话状态决定认证方式:
- 已登录会话:直接跳过认证页面,进入授权确认步骤。
- 未登录会话:展示登录表单,要求用户提供凭证(用户名/密码、MFA 等)。
- 会话过期:重新认证,同时保留原始授权请求参数。
认证完成后,授权服务器会生成一个临时的 授权码(Authorization Code),并绑定以下信息:
- 客户端 ID(
client_id) - 重定向 URI(
redirect_uri) - 请求的 scope 列表
- 认证用户标识(
sub) - 过期时间(通常为 1-10 分钟)
- 一次性使用标记
2.4 阶段三:授权码发放(Authorization Code Issuance)
授权服务器通过 302 重定向将用户代理送回客户端注册的 redirect_uri,并在 URL 查询参数中附带授权码:
HTTP/1.1 302 Found
Location: https://client.example.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyzABC123
客户端收到回调后,必须立即执行以下校验:
- 验证
state值与发起请求时保存的值一致。 - 确认
redirect_uri与注册值匹配。 - 记录授权码,准备发起令牌交换请求。
2.5 阶段四:令牌交换(Token Exchange)
客户端使用授权码向授权服务器的 /token 端点发起 POST 请求,换取 Access Token、ID Token 和可选的 Refresh Token。此阶段必须通过后端通道完成,避免令牌暴露给用户代理。
令牌交换请求示例:
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https://client.example.com/callback
&client_id=my-client-id
&client_secret=my-client-secret
授权服务器返回的响应:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"id_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "def50200d4b7f..."
}
| 返回字段 | 说明 | 生命周期 |
|---|---|---|
access_token |
用于访问资源服务器的令牌 | 通常 1 小时 |
id_token |
JWT 格式的用户身份声明 | 与 access_token 一致 |
refresh_token |
用于获取新 access_token 的长期令牌 | 数天至数月 |
2.6 阶段五:令牌刷新(Token Refresh)
当 Access Token 过期时,客户端可以使用 Refresh Token 获取新的 Access Token,无需用户重新认证。刷新请求同样通过后端通道完成:
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=def50200d4b7f...
&client_id=my-client-id
&client_secret=my-client-secret
刷新响应与令牌交换响应结构相同,但通常不包含新的 ID Token(除非 scope 包含 openid 且服务器配置为重新签发)。
2.7 阶段六:令牌撤销(Token Revocation)
当用户登出、客户端检测到安全事件或令牌泄露时,应主动撤销令牌。OIDC 提供 /revoke 端点用于撤销 Access Token 或 Refresh Token:
POST /revoke HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
token=eyJhbGciOiJSUzI1NiIs...
token_type_hint=access_token
&client_id=my-client-id
&client_secret=my-client-secret
撤销操作的注意事项:
- 幂等性:撤销已过期或不存在的令牌应返回成功(HTTP 200),避免客户端因重复撤销而报错。
- 级联撤销:撤销 Refresh Token 时,授权服务器应同时撤销由其派生的所有 Access Token。
- 异步通知:在分布式系统中,令牌撤销事件应通过消息队列广播到所有服务实例,确保缓存中的令牌被及时清除。
2.8 生命周期状态机
下图展示了授权码流程中令牌的完整状态转换,帮助理解各阶段之间的依赖关系:
2.9 异步编排中的生命周期管理策略
在异步任务编排中,令牌生命周期管理需要关注以下策略:
| 阶段 | 异步策略 | 关键指标 |
|---|---|---|
| 授权请求 | 使用异步任务生成并存储 state 和 nonce |
回调匹配成功率 > 99.9% |
| 令牌交换 | 封装为可重试的异步 RPC 调用,设置超时和重试次数 | 交换延迟 < 500ms (P99) |
| 令牌刷新 | 预刷新机制:在令牌过期前 10% 时间发起异步刷新 | 刷新成功率 > 99.5% |
| 令牌撤销 | 通过消息队列广播撤销事件,异步清除分布式缓存 | 撤销传播延迟 < 1s |
TokenManager 服务,对外提供统一的异步接口(如 getValidToken()、refreshToken()、revokeToken())。内部使用状态机和定时器自动处理令牌过期和刷新逻辑,上层业务无需关心令牌的具体状态。