OAuth 2 简介

更新于 2026-01-06

Mitchell Anicas 2021-07-29

OAuth 2 简介

引言

OAuth 2 是一种授权框架,允许应用程序(如 Facebook、GitHub 和 DigitalOcean)获得对用户在 HTTP 服务上账户的有限访问权限。它通过将用户身份验证委托给托管用户账户的服务,并授权第三方应用访问该账户来实现这一目标。OAuth 2 为 Web 应用、桌面应用以及移动设备提供了多种授权流程。

本指南面向应用程序开发者,概述了 OAuth 2 的角色、授权许可类型、使用场景和工作流程。


OAuth 角色

OAuth 定义了四种角色:

  • 资源所有者(Resource Owner):即授权应用程序访问其账户的用户。应用程序对用户账户的访问权限受限于所授予的授权范围(例如只读或读写权限)。
  • 客户端(Client):即希望访问用户账户的应用程序。在访问之前,必须获得用户的授权,并且该授权需经 API 验证。
  • 资源服务器(Resource Server):托管受保护用户账户的服务器。
  • 授权服务器(Authorization Server):验证用户身份后,向应用程序颁发访问令牌(access token)。

从应用开发者的角度来看,服务的 API 同时承担了资源服务器和授权服务器的角色。我们将这两个角色合称为服务API


抽象协议流程

了解了 OAuth 的角色之后,我们来看一下它们之间的一般交互流程:

Abstract Protocol Flow

抽象协议流程图

以下是图中各步骤的详细说明:

  1. 应用程序向用户请求授权,以访问服务中的资源。
  2. 如果用户授权了该请求,应用程序将收到一个授权许可(authorization grant)
  3. 应用程序向授权服务器(API)请求访问令牌,同时提供自身的身份凭证和授权许可。
  4. 如果应用程序的身份已通过验证,且授权许可有效,授权服务器(API)将向应用程序颁发一个访问令牌。至此,授权完成。
  5. 应用程序向资源服务器(API)请求资源,并出示访问令牌进行身份验证。
  6. 如果访问令牌有效,资源服务器(API)将把资源返回给应用程序。

实际流程会因所使用的授权许可类型而有所不同,但这是总体思路。我们将在后续章节中探讨不同的授权许可类型。


应用注册

在将 OAuth 用于你的应用程序之前,你必须先在服务提供商处注册你的应用。这通常通过服务网站的开发者或 API 部分的注册表单完成,你需要提供以下信息(可能还包括应用的其他详情):

  • 应用名称(Application Name)
  • 应用网站(Application Website)
  • 重定向 URI(Redirect URI)或回调 URL(Callback URL)

重定向 URI 是服务在用户授权(或拒绝)你的应用后将用户重定向到的位置,因此它必须是你应用中能处理授权码或访问令牌的部分。


客户端 ID 与客户端密钥

注册完成后,服务将为你颁发客户端凭证,包括客户端 ID(Client ID)客户端密钥(Client Secret)

  • 客户端 ID 是一个公开暴露的字符串,由服务 API 用来识别你的应用,也用于构建呈现给用户的授权 URL。
  • 客户端密钥 用于在应用请求访问用户账户时,向服务 API 证明自身身份,必须严格保密,仅限应用与 API 之间共享。

授权许可(Authorization Grant)

在前述抽象协议流程中,前四个步骤涉及获取授权许可和访问令牌。授权许可的类型取决于应用请求授权的方式以及 API 所支持的许可类型。

OAuth 2 定义了三种主要的授权许可类型,各自适用于不同场景:

  • 授权码(Authorization Code):用于服务器端应用
  • 客户端凭证(Client Credentials):用于具有 API 访问权限的应用
  • 设备码(Device Code):用于无浏览器或输入受限的设备

警告:OAuth 框架还规定了另外两种许可类型——隐式流程(Implicit Flow)密码许可(Password Grant)。然而,这两种类型均被认为不安全,不再推荐使用

接下来,我们将详细介绍各种授权许可类型的使用场景和流程。


授权许可类型:授权码(Authorization Code)

授权码许可是最常用的类型,因为它专为服务器端应用优化——这类应用的源代码不会公开暴露,能够安全地保管客户端密钥。这是一种基于重定向的流程,意味着应用必须能够与用户代理(即用户的 Web 浏览器)交互,并通过用户代理接收 API 授权码。

授权码流程

Authorization code flow

步骤 1 —— 构建授权码链接

首先,用户会看到一个类似如下的授权链接:

https://cloud.digitalocean.com/v1/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read

各参数说明:

  • https://cloud.digitalocean.com/v1/oauth/authorize:API 的授权端点
  • client_id=CLIENT_ID:应用的客户端 ID(API 用它识别应用)
  • redirect_uri=CALLBACK_URL:授权后服务将用户代理重定向到的地址
  • response_type=code:表明应用请求的是授权码许可
  • scope=read:指定应用请求的访问权限级别

步骤 2 —— 用户授权应用

用户点击链接后,需先登录服务以验证身份(若尚未登录)。随后,服务会提示用户授权或拒绝该应用访问其账户。下图是 DigitalOcean 的授权界面示例:

此处为 DigitalOcean 授权屏幕截图,显示 Thedropletbook 应用请求对 manicas@digitalocean.com 账户的只读权限

步骤 3 —— 应用接收授权码

如果用户点击“授权应用”,服务会将用户代理重定向到注册时指定的回调 URL,并附带一个授权码。例如:

https://dropletbook.com/callback?code=AUTHORIZATION_CODE

步骤 4 —— 应用请求访问令牌

应用向 API 的令牌端点发送 POST 请求,提交授权码及自身凭证(包括客户端密钥),以换取访问令牌。例如向 DigitalOcean 发送的请求:

POST https://cloud.digitalocean.com/v1/oauth/token
参数:
client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
&grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=CALLBACK_URL

步骤 5 —— 应用接收访问令牌

如果授权有效,API 将返回包含访问令牌(以及可选的刷新令牌)的响应,例如:

{
  "access_token": "ACCESS_TOKEN",
  "token_type": "bearer",
  "expires_in": 2592000,
  "refresh_token": "REFRESH_TOKEN",
  "scope": "read",
  "uid": 100101,
  "info": {
    "name": "Mark E. Mark",
    "email": "mark@thefunkybunch.com"
  }
}

此时,应用已完成授权。它可在令牌有效期内(或未被撤销前),根据授权范围使用该令牌访问用户账户。如果颁发了刷新令牌,可在原令牌过期后用于获取新的访问令牌。


关于 PKCE(Proof Key for Code Exchange)

如果公共客户端(如移动端或单页应用)使用授权码流程,授权码可能被拦截。PKCE(发音为“pixie”)是授权码流程的一项扩展,可缓解此类攻击。

PKCE 的工作原理如下:

  1. 客户端在每次授权请求时生成一个**代码验证器(code verifier)**并保存。
  2. 客户端将该验证器通过特定算法转换为代码挑战(code challenge),并在授权请求中一并发送挑战值和转换方法。
  3. 授权服务器记录挑战值和方法,并正常返回授权码。
  4. 客户端在后续的令牌请求中附上原始的代码验证器。
  5. 授权服务器使用相同的算法将验证器转换为挑战值,并与之前记录的挑战值比对。若不匹配,则拒绝请求。

建议所有客户端都使用 PKCE 以提升安全性。


授权许可类型:客户端凭证(Client Credentials)

客户端凭证许可允许应用访问自身的服务账户。适用场景包括:应用需要更新其注册描述或重定向 URI,或通过 API 访问存储在其服务账户中的其他数据。

客户端凭证流程

应用通过向授权服务器发送其客户端 ID 和密钥来请求访问令牌。例如:

POST https://oauth.example.com/token
参数:
grant_type=client_credentials
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET

如果凭证验证通过,授权服务器将返回访问令牌。此时,应用即可代表自身账户进行操作。

注意:DigitalOcean 目前不支持客户端凭证许可类型,因此上述链接指向的是虚构的授权服务器 oauth.example.com


授权许可类型:设备码(Device Code)

设备码许可适用于无浏览器或输入受限的设备(如智能电视、游戏主机),使用户能更方便地授权这些设备访问其账户。

设备码流程

  1. 用户在设备(如电视)上启动应用。

  2. 应用向设备授权端点发送 POST 请求,例如:

    POST https://oauth.example.com/device
    client_id=CLIENT_ID
    
  3. 该端点不验证设备身份,而是返回以下信息:

    • device_code:用于标识设备的唯一代码
    • user_code:用户需在另一台设备(如手机或电脑)上输入的验证码
    • verification_uri:用户输入验证码的网址
    • interval:建议的轮询间隔(秒)
    • expires_in:设备码有效期(秒)

    示例响应:

    {
      "device_code": "IO2RUI3SAH0IQuESHAEBAeYOO8UPAI",
      "user_code": "RSIK-KRAM",
      "verification_uri": "https://example.okta.com/device",
      "interval": 10,
      "expires_in": 1600
    }
    

    注:设备码也可表示为二维码,供移动设备扫描。

  4. 用户在电脑或手机上访问 verification_uri,输入 user_code 并登录账户,随后在同意界面上授权该设备。

  5. 与此同时,设备会定期轮询访问令牌端点,直到获得令牌或收到错误(如 authorization_pendingslow_downaccess_deniedexpired_token)。

  6. 若用户授权成功,访问令牌端点将返回访问令牌。

注意:DigitalOcean 目前不支持设备码许可类型,示例中的链接为虚构地址。


访问令牌使用示例

获得访问令牌后,应用即可在令牌有效期内(或未被撤销前),根据授权范围通过 API 访问用户账户。

以下是一个使用 curl 的 API 请求示例,其中包含访问令牌:

curl -X POST \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://api.digitalocean.com/v2/$OBJECT"

只要访问令牌有效,API 就会按规范处理请求。若令牌过期或无效,API 将返回 invalid_request 错误。


刷新令牌流程(Refresh Token Flow)

当访问令牌过期后,继续使用它调用 API 会返回“无效令牌”错误。此时,如果当初颁发访问令牌时也提供了刷新令牌(refresh token),则可用它向授权服务器申请新的访问令牌。

示例请求:

POST https://cloud.digitalocean.com/v1/oauth/token
参数:
grant_type=refresh_token
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
&refresh_token=REFRESH_TOKEN

结语

通过本指南,你应该已经理解了 OAuth 2 的基本工作原理,并掌握了在不同场景下应选择哪种授权流程。