OAuth2 协议介绍

发布时间: 更新时间: 总字数:3230 阅读时间:7m 作者: IP上海 分享 网址

OAuth 2.0 是一种授权框架(Authorization Framework),用于允许第三方应用在用户授权后,代表用户访问受保护的资源(如用户存储在服务提供方的数据),而无需直接共享用户的凭据(如用户名和密码)。它是现代互联网中广泛使用的协议,支持单点登录(SSO)、API 访问控制等场景。

基础

相关角色

OAuth 2.0 定义了以下关键角色:

  1. 资源所有者(Resource Owner):通常是用户,拥有对受保护资源的控制权
  2. 客户端(Client):请求访问资源的第三方应用(如网站、移动应用)
  3. 授权服务器(Authorization Server):验证用户身份并颁发访问令牌(Access Token)
  4. 资源服务器(Resource Server):存储受保护资源的服务(如 API 服务器),根据访问令牌决定是否授权请求

授权类型(Grant Types)

OAuth 2.0 支持多种授权方式,适用于不同场景:

  1. 授权码模式(Authorization Code)

    • 最安全的模式,适用于 Web 应用和移动应用
    • 客户端通过前端渠道获取授权码,再通过后端渠道用授权码换取令牌
    • 支持刷新令牌(Refresh Token),避免用户频繁登录
  2. 简化模式(Implicit)

    • 适用于纯前端应用(如单页应用 SPA)
    • 直接在前端返回访问令牌(无授权码步骤),但安全性较低
  3. 密码模式(Resource Owner Password Credentials)

    • 用户直接将用户名/密码提供给客户端(仅适用于高度信任的客户端,如自家应用)
    • 风险较高,需谨慎使用
  4. 客户端凭证模式(Client Credentials)

    • 客户端用自己的身份(而非用户)获取令牌,适用于服务器间通信(如内部服务)
  5. 扩展模式(如 PKCE)

    • 增强移动端或公共客户端的安全性,防止授权码劫持

核心概念

  1. 访问令牌(Access Token)

    • 短期的令牌,用于访问资源
    • 通常通过 HTTPS 传输,避免泄露
  2. 刷新令牌(Refresh Token)

    • 长期令牌,用于获取新的访问令牌(无需用户重新授权)
    • 存储需严格保护(如服务器端)
  3. Scope(权限范围)

    • 定义客户端请求的权限(如 read:profilewrite:file
  4. 令牌有效期

    • 访问令牌通常较短(如 1 小时),刷新令牌较长(如 30 天)

流程

OAuth 2.0 的典型流程分为以下步骤:

  1. 用户发起授权请求:客户端将用户重定向到授权服务器,请求访问特定资源
  2. 用户授权:用户在授权服务器上登录并同意客户端的访问请求
  3. 颁发授权许可(Authorization Grant):授权服务器返回一个授权许可(如授权码)给客户端
  4. 客户端获取访问令牌:客户端将授权许可发送给授权服务器,换取访问令牌(Access Token)
  5. 访问资源:客户端使用访问令牌向资源服务器发起请求,资源服务器验证令牌后返回数据

授权码模式

     +----------+
     | Resource |
     |   Owner  |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier      +---------------+
     |         -+----(A)-- & Redirection URI ---->|               |
     |  User-   |                                 | Authorization |
     |  Agent  -+----(B)-- User authenticates --->|     Server    |
     |          |                                 |               |
     |         -+----(C)-- Authorization Code ---<|               |
     +-|----|---+                                 +---------------+
       |    |                                         ^      v
      (A)  (C)                                        |      |
       |    |                                         |      |
       ^    v                                         |      |
     +---------+                                      |      |
     |         |>---(D)-- Authorization Code ---------'      |
     |  Client |          & Redirection URI                  |
     |         |                                             |
     |         |<---(E)----- Access Token -------------------'
     +---------+       (w/ Optional Refresh Token)

   Note: The lines illustrating steps (A), (B), and (C) are broken into
   two parts as they pass through the user-agent.

                     Figure 3: Authorization Code Flow

以下是 OAuth 2.0 的典型流程图(以最常用的 授权码模式 为例),展示了客户端、用户、授权服务器和资源服务器之间的交互:

+--------+          +--------------+          +---------------+
|        |          |              |          |               |
| 用户    |          |  客户端       |          | 授权服务器     |
|        |          |  (Client)    |          |               |
+---+----+          +-----+---+----+          +-------+-------+
    |                      |                           |
    | 1. 访问客户端          |                           |
    +--------------------->|                           |
    |                      |                           |
    | 2. 重定向到授权服务器   |                           |
    |<---------------------+                           |
    | (携带 client_id, redirect_uri, scope, state)    |
    |                      |                           |
    | 3. 用户登录并授权       |                           |
    +--------------------->|                           |
    |                      |                           |
    | 4. 返回授权码         |                           |
    | (重定向到 redirect_uri,附带 code 和 state)       |
    |<---------------------+                           |
    |                      |                           |
    | 5. 用授权码换取令牌     |                           |
    |                      +-------------------------->|
    |                      | 6. 验证并返回访问令牌         |
    |                      | (access_token, refresh_token)|
    |                      |<--------------------------+
    |                      |                           |
    | 7. 使用访问令牌访问资源 |                           |
    |                      +-------------------------->|
    |                      |                           |  +--------------+
    |                      |                           +->| 资源服务器     |
    |                      |                           |  |              |
    |                      | 8. 返回受保护资源            |  +--------------+
    |                      |<--------------------------+
    |                      |                           |
+---+----+          +------+-------+          +--------+------+
|        |          |              |          |               |
| 用户    |          |  客户端       |          | 授权服务器     |
|        |          |  (Client)    |          |               |
+--------+          +--------------+          +---------------+
  1. 用户访问客户端:用户通过浏览器访问客户端应用(例如一个第三方网站)

  2. 客户端重定向用户到授权服务器:客户端将用户重定向到授权服务器的授权端点,并携带以下参数:

    • client_id:客户端的唯一标识
    • redirect_uri:授权成功后重定向的 URI
    • scope:请求的权限范围(如 read:profile
    • response_type=code:表示使用授权码模式
    • state:随机生成的防 CSRF 令牌(可选但推荐)
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
		&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
  1. 用户登录并授权:用户在授权服务器上登录(例如输入用户名密码),并同意客户端请求的权限

  2. 授权服务器返回授权码:授权服务器生成一个短期有效的 授权码(Authorization Code),通过重定向 URI 返回给客户端(附加到 URL 参数中),同时返回 state 参数(如果客户端发送了的话)

# 成功
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
					&state=xyz

# 失败
HTTP/1.1 302 Found
Location: https://client.example.com/cb?error=access_denied&state=xyz
  1. 客户端用授权码换取访问令牌:客户端通过后端服务将授权码发送到授权服务器的令牌端点,并附带以下参数:

    • grant_type=authorization_code:表示授权类型为授权码模式
    • code:上一步收到的授权码
    • redirect_uri:必须与步骤 2 中的一致
    • client_idclient_secret:客户端凭证(用于身份验证)
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
  1. 授权服务器返回访问令牌:授权服务器验证授权码和客户端身份后,返回:

    • access_token:用于访问资源的令牌
    • refresh_token(可选):用于刷新访问令牌的长期令牌
    • expires_in:访问令牌的有效期(秒)
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
	"access_token":"2YotnFZFEjr1zCsicMWpAA",
	"token_type":"example",
	"expires_in":3600,
	"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
	"example_parameter":"example_value"
}
  1. 客户端访问资源服务器:客户端使用 access_token 访问资源服务器(例如调用 API),资源服务器验证令牌的有效性

  2. 资源服务器返回数据:如果令牌有效,资源服务器返回请求的受保护资源(如用户数据)

  3. 刷新授权码

  +--------+                                           +---------------+
  |        |--(A)------- Authorization Grant --------->|               |
  |        |                                           |               |
  |        |<-(B)----------- Access Token -------------|               |
  |        |               & Refresh Token             |               |
  |        |                                           |               |
  |        |                            +----------+   |               |
  |        |--(C)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(D)- Protected Resource --| Resource |   | Authorization |
  | Client |                            |  Server  |   |     Server    |
  |        |--(E)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(F)- Invalid Token Error -|          |   |               |
  |        |                            +----------+   |               |
  |        |                                           |               |
  |        |--(G)----------- Refresh Token ----------->|               |
  |        |                                           |               |
  |        |<-(H)----------- Access Token -------------|               |
  +--------+           & Optional Refresh Token        +---------------+

               Figure 2: Refreshing an Expired Access Token
  • 请求
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
&client_id=s6BhdRkqt3&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw
  • 响应同上

简化模式

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier     +---------------+
     |         -+----(A)-- & Redirection URI --->|               |
     |  User-   |                                | Authorization |
     |  Agent  -|----(B)-- User authenticates -->|     Server    |
     |          |                                |               |
     |          |<---(C)--- Redirection URI ----<|               |
     |          |          with Access Token     +---------------+
     |          |            in Fragment
     |          |                                +---------------+
     |          |----(D)--- Redirection URI ---->|   Web-Hosted  |
     |          |          without Fragment      |     Client    |
     |          |                                |    Resource   |
     |     (F)  |<---(E)------- Script ---------<|               |
     |          |                                +---------------+
     +-|--------+
       |    |
      (A)  (G) Access Token
       |    |
       ^    v
     +---------+
     |         |
     |  Client |
     |         |
     +---------+

   Note: The lines illustrating steps (A) and (B) are broken into two
   parts as they pass through the user-agent.

                       Figure 4: Implicit Grant Flow
  • 请求
GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
		&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
  • 响应
# 成功
HTTP/1.1 302 Found
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
					&state=xyz&token_type=example&expires_in=3600

# 失败
HTTP/1.1 302 Found
Location: https://client.example.com/cb#error=access_denied&state=xyz

密码模式

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          v
          |    Resource Owner
         (A) Password Credentials
          |
          v
     +---------+                                  +---------------+
     |         |>--(B)---- Resource Owner ------->|               |
     |         |         Password Credentials     | Authorization |
     | Client  |                                  |     Server    |
     |         |<--(C)---- Access Token ---------<|               |
     |         |    (w/ Optional Refresh Token)   |               |
     +---------+                                  +---------------+

            Figure 5: Resource Owner Password Credentials Flow
  • 请求
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w
  • 响应
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
	"access_token":"2YotnFZFEjr1zCsicMWpAA",
	"token_type":"example",
	"expires_in":3600,
	"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
	"example_parameter":"example_value"
}

客户端凭证模式

     +---------+                                  +---------------+
     |         |                                  |               |
     |         |>--(A)- Client Authentication --->| Authorization |
     | Client  |                                  |     Server    |
     |         |<--(B)---- Access Token ---------<|               |
     |         |                                  |               |
     +---------+                                  +---------------+

                     Figure 6: Client Credentials Flow
  • 请求
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
  • 响应
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
	"access_token":"2YotnFZFEjr1zCsicMWpAA",
	"token_type":"example",
	"expires_in":3600,
	"example_parameter":"example_value"
}

Extension Grants

  • 请求
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Asaml2-
bearer&assertion=PEFzc2VydGlvbiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDU
[...omitted for brevity...]aG5TdGF0ZW1lbnQ-PC9Bc3NlcnRpb24-
  • 响应
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
	"access_token":"2YotnFZFEjr1zCsicMWpAA",
	"token_type":"example",
	"expires_in":3600,
	"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
	"example_parameter":"example_value"
}

安全性

  • HTTPS 强制要求:所有令牌传输必须通过 HTTPS
  • 令牌泄露风险:访问令牌可能被截获,需设置较短有效期
  • CSRF 防护:使用 state 参数防止跨站请求伪造
  • 客户端身份验证:机密客户端(如 Web 服务端)需通过 client_idclient_secret 验证

常见场景

  1. 社交登录:用户使用 Google/Facebook 账号登录第三方应用
  2. API 访问:第三方应用访问用户存储在服务商的数据(如获取 GitHub 仓库列表)
  3. 微服务通信:服务间通过客户端凭证模式授权

与 OAuth 1.0 的区别

  • OAuth 2.0 不再强制使用加密签名,而是依赖 HTTPS
  • 更简洁的流程,支持移动端和单页应用
  • 分离了角色(授权服务器和资源服务器)

常见库与工具

  • 服务端:Spring Security OAuthAuth0Keycloak
  • 客户端:Passport.jsOAuth2-client(Python)AppAuth(移动端)

总结

OAuth 2.0 通过授权机制平衡了安全性与便利性,成为现代应用授权的标准。开发者需根据场景选择合适的授权类型,并严格遵循安全实践(如保护令牌、使用 HTTPS)。对于身份认证需求,可结合 OpenID Connect(基于 OAuth 2.0 的扩展协议)实现

参考

  1. https://oauth.net/2/
  2. OAuth 2.0 Framework(RFC 6749)
  3. Bearer Tokens(RFC 6750)
  4. Threat Model and Security Considerations(RFC 6819)
  5. OAuth Security Best Current Practice(RFC 9700)
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数