Refinition

OAuth2 是在WEB基础上发展出来的一个授权框架(Authorization Framework),也可以认为它是一套协议,一套能解决第三方授权问题的解决方案,优势在于它允许第三方应用在不获取用户密码的情况下,获得访问用户资源(用户的ID信息等)的有限权限。

它是如何做到的?下面来解析一下这个协议。

搞懂这个协议,需要理解下面几个部分:

四个核心角色

OAuth2 流程中有四个关键角色:

  • 资源所有者 (Resource Owner)
    用户(一般指使用客户端的人),他拥有受保护资源(一般指他的账号信息);

  • 客户端 (Client)
    一个前端UI应用,方便用户操作的面板。一般来说客户端和授权服务器是前后端关系。

  • 授权服务器 (Authorization Server)
    验证用户身份并颁发令牌的 绝对控制者。

  • 资源服务器 (Resource Server)
    一般指可以提供给用户使用的服务器,能给用户提供专属服务,比如网盘服务、点外卖服务,这种也叫第三方应用。
    资源服务器一般需要用户信息,才能提供服务。
    获得用户信息,可以让用户注册,但是APP太多了,很多用户不愿意注册了。就发展出第三方应用去授权服务器拿取用户ID的免注册模式。

四类常见授权模式

OAuth2 定义了多种授权方式,适应不同场景,下面看4个常见:

  • 授权码模式 (Authorization Code) - 最常用、较安全(本文讨论这个模式)
  • 密码模式 (Resource Owner Password Credentials) - 需要信任客户端
  • 客户端凭证模式 (Client Credentials) - 应用访问自己的资源
  • 隐式模式 (Implicit) - 较不安全,已不推荐

授权模式中的术语

  • 令牌 (Tokens)

    • 访问令牌 (Access Token):相当于"临时门禁卡",用于访问资源
    • 刷新令牌 (Refresh Token):用于获取新的访问令牌,避免用户频繁重新登录
  • ......


OAuth2 协议流程解析

在看时序图之前,必须先清楚各个角色的作用,不然就会混乱。

时序图,简版流程:

sequenceDiagram actor User as 用户 participant Client as 客户端应用 participant AuthServer as 授权服务器 participant ResourceServer as 资源服务器 Note over User, Client: 1. 启动授权流程 User->>Client: 访问应用 Client->>User: 重定向到授权服务器 Note over User, AuthServer: 2. 用户认证与授权 User->>AuthServer: 在授权页面登录并授权 AuthServer->>User: 重定向回应用并携带授权码 Note over Client, AuthServer: 3. 交换令牌(后端通信) Client->>AuthServer: 发送授权码 + 客户端凭证 AuthServer->>Client: 返回访问令牌 Note over Client, ResourceServer: 4. 访问资源 Client->>ResourceServer: 使用访问令牌请求资源 ResourceServer->>Client: 返回受保护资源 Client->>User: 显示用户请求的内容

更接近实际的流程,以企微授权一个工作台应用案例为例子:

sequenceDiagram actor 用户 participant 客户端 as 企业微信客户端<br>(浏览器/WebView) participant 应用 as 企微工作台应用<br>(Your Server) participant 授权服务器 as 企业微信<br>授权服务器 participant 资源服务器 as 企业微信<br>资源服务器 用户->>客户端: 1. 点击工作台应用图标 客户端->>应用: 2. 访问应用首页 (GET /) Note over 应用, 授权服务器: 认证与授权阶段 应用->>客户端: 3. 重定向到企微授权页<br>?response_type=code&redirect_uri=... 客户端->>授权服务器: 4. 跳转到授权页面 授权服务器->>用户: 5. (可选) 向用户显示授权同意页面 用户->>授权服务器: 6. 点击同意授权 授权服务器->>客户端: 7. 重定向到 redirect_uri 并携带 code 客户端->>应用: 8. 请求 Callback URL (GET /callback?code=...) Note over 应用, 资源服务器: 获取访问令牌与用户信息 应用->>授权服务器: 9. 用 code 换取 access_token<br>(POST /token) + secret 授权服务器->>应用: 10. 返回 access_token 应用->>资源服务器: 11. 使用 access_token 获取用户信息<br>(GET /userinfo?access_token=...) 资源服务器->>应用: 12. 返回用户信息 (e.g., UserId) Note over 应用, 用户: 正常业务访问 应用->>应用: 13. 根据UserId处理业务逻辑<br>(e.g., 查询数据库) 应用->>客户端: 14. 返回个性化应用页面 客户端->>用户: 15. 显示应用内容

以上都是以一个用户视角看的流程,其实后端之间还做了很多事情,才能保障整个流程的安全,不过下面的流程图需要以一个开发者的角度看待,下面继续解构。


后端之间的交互流程-包含安全方案设计

下面以企业微信OAuth2授权码流程,以开发者(应用服务器/资源服务器)的视角,看待应用如何与企微授权服务器建立联系,包括如何获取安全凭证(如Secret)、验证的流程。

企业微信OAuth2授权码流程中,开发者需要先在企微管理后台创建应用,获取应用的安全凭证(corp_id, secret等),然后在应用服务器中使用这些凭证。

整体流程分为两个主要部分:

  • 应用注册与安全凭证发放(静态配置)
  • OAuth2授权码流程(动态交互)

理解几个密码学上关键的工具包:

  • AgentId
  • Secret
  • 随机数
  • 签名-HMAC
  • ......

应用注册与安全凭证发放

这一步是确保整体安全的关键流程,Secret参数作为一个关键密钥(可以申请更换),它也是标识身份,防抵赖性作用。
一个AgentId 对应 一把Secret密钥。
Secret必须安全地存储在应用服务器内,只做打签名用。不可以暴露,否则有被冒充安全风险!

sequenceDiagram participant 开发者 participant 企微管理后台 participant 应用服务器 开发者->>企微管理后台: 1. 登录管理后台 开发者->>企微管理后台: 2. 创建应用(设置应用名称、logo等) 企微管理后台->>开发者: 3. 返回应用凭证:AgentId、Secret等 开发者->>应用服务器: 4. 配置AgentId和Secret

OAuth2授权码流程

State:
客户端生成 State 流程:

  1. 收集数据:随机数 + 时间戳 + 会话ID + 业务参数
  2. 构建结构:
  3. 计算签名:HMAC-SHA256(排序后的数据字符串, client_secret)
  4. 添加签名:
  5. Base64编码:生成最终的state参数

客户端/服务端验证 State 流程:

  1. Base64解码state,得到原始数据结构
  2. 提取签名值,从数据结构中移除签名字段
  3. 重新计算签名:HMAC-SHA256(相同的排序数据, client_secret)
  4. 对比签名:恒定时间比较计算签名与提取签名
  5. 如果一致 → 数据未被篡改,验证通过
  6. 如果不一致 → 可能被篡改,拒绝请求
graph TB A[客户端生成state] --> B[使用client_secret签名] C[客户端验证state] --> D[使用client_secret验签] B --> E[确保数据完整性] D --> E

再次看OAuth2授权码流程

流程图是AI生成的,有些角色关系描述得不是那么准确,但是大致理解state、code的安全流程是没问题的。请辩证的看。

sequenceDiagram participant User as 用户 participant Client as 客户端应用 participant Browser as 浏览器 participant AuthServer as 授权服务器 participant TokenStore as 令牌存储 Note over Client, AuthServer: State 生成与签名阶段 User->>Client: 1. 访问应用 Client->>Client: 2. 生成 State 参数 Note right of Client: - 收集会话ID<br/>- 生成随机数<br/>- 记录时间戳<br/>- 添加业务上下文 Client->>Client: 3. State 签名 Note right of Client: - 排序数据字段<br/>- 计算HMAC签名<br/>- Base64编码 Client->>Browser: 4. 存储State到Session Client->>Browser: 5. 重定向到授权页(带state) Note over Browser, AuthServer: 用户认证与授权阶段 Browser->>AuthServer: 6. 请求授权页面 AuthServer->>User: 7. 显示登录页面 User->>AuthServer: 8. 输入凭证并授权 AuthServer->>AuthServer: 9. 验证用户凭证 Note over AuthServer, TokenStore: Code 生成阶段 AuthServer->>AuthServer: 10. 生成授权码(Code) Note right of AuthServer: - 关联用户ID<br/>- 绑定客户端ID<br/>- 设置权限范围<br/>- 添加时间戳 AuthServer->>TokenStore: 11. 存储Code元数据 Note right of TokenStore: - code值<br/>- 用户信息<br/>- 客户端信息<br/>- 过期时间 AuthServer->>Browser: 12. 重定向回客户端(带code+state) Note over Client, TokenStore: State 校验与 Code 验证阶段 Browser->>Client: 13. 请求回调URL Client->>Client: 14. 提取并验证State Note right of Client: - Base64解码<br/>- 验证签名<br/>- 检查时效性<br/>- 会话匹配 Client->>Client: 15. 清理已用State Client->>AuthServer: 16. 用Code交换Token AuthServer->>TokenStore: 17. 验证Code有效性 TokenStore->>AuthServer: 18. 返回Code关联信息 AuthServer->>AuthServer: 19. Code使用标记 AuthServer->>Client: 20. 返回访问令牌 TokenStore->>TokenStore: 21. 清理已用Code Client->>User: 22. 完成授权流程

为什么用code不直接返回token?

授权码(code)的安全作用介绍:

授权码(code)是OAuth2授权码流程中的核心凭证,它通过前端信道传递(浏览器重定向),然后由客户端应用在后端信道中与授权服务器交换访问令牌。授权码的安全作用主要包括:

  • 避免访问令牌通过前端信道传输,降低令牌泄露风险。
  • 授权码是短暂的,一次性使用的,降低被窃取后的风险。
  • 授权码与客户端身份绑定,防止被其他客户端使用。

假设code被截取:

  • 即使授权码被窃取,攻击窗口很短
  • 自动清理过期的授权码,减少存储负担

所以code也不能保证绝对安全,但是相对安全,防CSRF类攻击时,它是有效的。

posted on 2025-10-30 14:44  Mysticbinary  阅读(167)  评论(0)    收藏  举报