OAuth 2.0授权的4种方式(非常详细)
作用域(Scope)是 OAuth 2.0 中的一种机制,用于限制应用程序对用户账户的访问。第三方应用程序可以请求一个或多个作用域,然后将请求的作用域展示给用户,在用户同意授权之后,系统颁发给该应用程序的访问令牌将被限制于所授予的作用域范围内。
OAuth 通过引入授权层,将客户的角色与资源的角色分开。在 OAuth 中,客户端请求访问受控资源,资源所有者同意以后,资源服务器向客户端颁发令牌,客户端通过持有该令牌去请求数据。
OAuth 的核心就是向第三方应用颁发令牌,OAuth 2.0 规定了四种获得令牌的流程,你可以选择最适合自己的那一种向第三方应用颁发令牌。最新的四种授权方式是授权码(Authorization Code)、客户端凭证(Client Credentials)、设备码(Device Code)、密码式(Refresh Token)。
此外,还有一种授权方式:隐式授权(Implicit Code),在之前比较推荐用于原生应用和用户代理应用中请求访问授权,但是因为涉及安全风险(包括密码式授权方式),现在已经过时,不再推荐使用,而是推荐基于授权码的扩展 PKCE 的授权方式。
不管哪一种授权方式,在第三方应用申请令牌之前都必须先到系统备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(Client ID)和客户端密钥(Client Secret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。
接下来依次解释一下这四种授权方式。
这是目前流行的推荐方式,这种方式总共是 2 个请求加 2 个响应,即(一个授权请求+响应)和一个令牌请求+响应。这种方式是最常用的流程,安全性也最高,适用于那些有后端的 Web 应用。这种方式适用于下图所示的情形。
首先,客户端 Web 应用构建一个授权请求,跳转到授权端点。该请求地址路径如下所示:
当用户访问此授权请求时,会向用户询问是否允许授权,在用户允许后将返回授权响应。授权响应包含了需要用来获取访问令牌的授权码,响应参数如下表所示。
当用户批准请求后,授权服务器会将用户浏览器重定向到 redirect_uri,并添加相应参数,如下所示:
一旦获取到授权码,就立即向令牌端点发起令牌请求,例如:
令牌服务器端点收到该令牌请求时会颁发访问令牌(Access Token),具体做法是向令牌请求中给出的 redirect_uri 指定的网址发送一段 JSON 数据:
除了这四项响应参数以外,还可能返回其他的一些信息,具体需要参考对应的授权服务器提供商给出的 API 文档。以上就是使用授权码方式来获取访问令牌的详细请求和响应说明。
此外,授权码类型还有一个扩展类型:PKCE(RFC 7636)。它可以防止多种攻击并能够安全地从公共客户端执行 OAuth 交换,具体请访问官方文档(https://www.oauth.com/pkce/)。这种方式是最安全、最完善的,适用于保密的和公关的两种客户端类型。
发送令牌请求成功后,授权服务器颁发令牌,做出响应,响应内容与授权码方式的令牌响应内容一致。
首先设备向授权服务器注册获得设备代码(device_code),在用户允许授权之前,设备开始以固定时间间隔轮询请求访问令牌,直到用户允许授权或者拒绝。
设备代码方式发送的令牌请求如下:
密码式申请令牌请求的示例如下:
OAuth 通过引入授权层,将客户的角色与资源的角色分开。在 OAuth 中,客户端请求访问受控资源,资源所有者同意以后,资源服务器向客户端颁发令牌,客户端通过持有该令牌去请求数据。
OAuth 的核心就是向第三方应用颁发令牌,OAuth 2.0 规定了四种获得令牌的流程,你可以选择最适合自己的那一种向第三方应用颁发令牌。最新的四种授权方式是授权码(Authorization Code)、客户端凭证(Client Credentials)、设备码(Device Code)、密码式(Refresh Token)。
此外,还有一种授权方式:隐式授权(Implicit Code),在之前比较推荐用于原生应用和用户代理应用中请求访问授权,但是因为涉及安全风险(包括密码式授权方式),现在已经过时,不再推荐使用,而是推荐基于授权码的扩展 PKCE 的授权方式。
不管哪一种授权方式,在第三方应用申请令牌之前都必须先到系统备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(Client ID)和客户端密钥(Client Secret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。
接下来依次解释一下这四种授权方式。
授权码
授权码方式指的是第三方应用先申请一个授权码,然后用该码获取访问令牌。这是目前流行的推荐方式,这种方式总共是 2 个请求加 2 个响应,即(一个授权请求+响应)和一个令牌请求+响应。这种方式是最常用的流程,安全性也最高,适用于那些有后端的 Web 应用。这种方式适用于下图所示的情形。

首先,客户端 Web 应用构建一个授权请求,跳转到授权端点。该请求地址路径如下所示:
https://authorization-server.com/auth? response_type=code& client_id=29352735982374239857& redirect_uri=https://example-app.com/callback& scope=read& state=xcoivjuywkdkhvusuye3kch该授权请求的参数说明如下表所示:
请求参数(值) | 释义 | 要求 |
---|---|---|
response_type=code | 这个参数告诉授权服务器要求返回授权码 | 必需 |
client_id | 客户端应用标识,授权服务器根据这个参数来明确知道是谁在请求。这个参数值是开发人员通过向授权服务注册来获得的 | 必需 |
redirect_uri | 跳转地址,告诉授权服务器在批准请求后将用户发送回何处 | 可选 |
scope | 一个或多个用空格分隔的字符串,指示客户端应用请求的作用域 | 可选 |
state | 客户端状态,客户端应用通过生成一个随机字符串,将其包含在该请求中。然后,在用户授权应用后检查是否返回了相同的值,用于防止CSRF攻击使用 | 可选,推荐 |
当用户访问此授权请求时,会向用户询问是否允许授权,在用户允许后将返回授权响应。授权响应包含了需要用来获取访问令牌的授权码,响应参数如下表所示。
响应参数(值) | 释义 | 要求 |
---|---|---|
code | 返回的授权码 | 必需 |
state | 客户端状态,和请求时发送的 state 值一致。 | 如果请求中包含 state,那么响应中就必须包含 |
当用户批准请求后,授权服务器会将用户浏览器重定向到 redirect_uri,并添加相应参数,如下所示:
https://example-app.com/callback? code=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3& state=xcoiv98y2kd22vusuye3kchcode 有效期非常短,通常只有 1~10 分钟的有效期,这取决于 OAuth 服务。
一旦获取到授权码,就立即向令牌端点发起令牌请求,例如:
https://authentication-server.com/oauth/token? grant_type=authorization_code& code=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3& redirect_uri=https://example-app.com/redirect& client_id=xxxxxxxxxx& client_secret=xxxxxxxxxx该令牌请求参数释义如下表所示:
请求参数(值) | 释义 | 要求 |
---|---|---|
grant_type=authorization_code | 告诉令牌端点客户端应用正在使用授权码类型 | 必需 |
code | 客户端应用在授权响应重定向 URI 中获得的授权代码 | 必需 |
redirect_uri | 与授权请求中使用相同的重定向 URI。有些 API 不需要此参数,需要根据授权服务器提供的 API 文档来确定 | 可选 |
client_id | 客户端应用的标识(ID) | 必需 |
client_secret | 客户端应用的密钥。这样可以确保仅从客户端应用发出获取访问令牌的请求,而不是从可能已经拦截了授权代码的潜在攻击者发出请求 | 必需 |
令牌服务器端点收到该令牌请求时会颁发访问令牌(Access Token),具体做法是向令牌请求中给出的 redirect_uri 指定的网址发送一段 JSON 数据:
{ "access_token": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI", "token_type": "bearer", "expires_in": 3600, "refresh_token": "IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk", "scope": "read", "uid": 100101, "info": {...} }该令牌响应参数释义如下表所示:
响应参数 | 释义 | 要求 |
---|---|---|
access_token | 授权服务器分配的访问令牌 | 必需 |
token_type | 被授权服务器分配的令牌类型 | 必需 |
expires_in | 失效时间,访问令牌过多少秒后就不再有效 | 可选 |
refresh_token | 令牌过期后的刷新令牌。一旦 access token 失效,就可以通过访问令牌刷新端点,将 refresh token 传递过去获取一个新的 access token | 可选 |
除了这四项响应参数以外,还可能返回其他的一些信息,具体需要参考对应的授权服务器提供商给出的 API 文档。以上就是使用授权码方式来获取访问令牌的详细请求和响应说明。
此外,授权码类型还有一个扩展类型:PKCE(RFC 7636)。它可以防止多种攻击并能够安全地从公共客户端执行 OAuth 交换,具体请访问官方文档(https://www.oauth.com/pkce/)。这种方式是最安全、最完善的,适用于保密的和公关的两种客户端类型。
客户端凭证
当应用程序请求访问令牌而不是代表用户访问自己的资源时,将使用“客户端凭证”的方式获取访问令牌,这种方式是单个请求+响应。请求获取访问令牌的方式如下所示:https://authentication-server.com/oauth/token? grant_type=client_credentials& client_id=xxxxxxxxxx& client_secret=xxxxxxxxxx与授权码方式不同的是,grant_type 的值为 client_credentials,指定为客户端凭证方式,client_id 和 client_secret 两个参数与授权码方式令牌请求中的这两个参数是一样的,这两个参数也可以通过 HTTP Basic auth 方式放置在 Header 中提供。
发送令牌请求成功后,授权服务器颁发令牌,做出响应,响应内容与授权码方式的令牌响应内容一致。
设备代码
无浏览器或受输入限制的设备可以使用设备代码授权类型来将先前获得的设备代码交换为访问令牌。设备代码授权类型值为 urn:ietf:params:oauth:grant-type:device_code。首先设备向授权服务器注册获得设备代码(device_code),在用户允许授权之前,设备开始以固定时间间隔轮询请求访问令牌,直到用户允许授权或者拒绝。
设备代码方式发送的令牌请求如下:
https://authentication-server.com/oauth/token? grant_type=urn:ietf:params:oauth:grant-type:device_code& client_id=a17c21ed& device_code=NGU5OWFiNjQ5YmQwNGY3YTdmZTEyNzQ3YzQ1YSA其中的参数 device_code 就是设备代码。
密码式授权
密码式授权类型是一种将用户凭据交换为访问令牌的方式。因为客户端应用程序必须收集用户的密码并将其发送到授权服务器,所以不建议使用此授权。密码式授权只包含单个请求+响应。密码式申请令牌请求的示例如下:
https://authentication-server.com/oauth/token? grant_type=password& username=user@example.com& password=1234luggage& client_id=xxxxxxxxxx& client_secret=xxxxxxxxxx各项请求参数说明如下表所示:
请求参数(值) | 释义 | 要求 |
---|---|---|
grant_type="password" | 指定授权类型为密码式 | 必需 |
username | 用户名 | 必需 |
password | 用户密码 | 必需 |
scope | 客户端应用请求的作用域 | 可选 |
client_id | 客户端应用的标识(ID) | 必需 |
client_secret | 客户端应用的密钥。这样可以确保仅从客户端应用发出获取访问令牌的请求,而不是从可能已经拦截了授权代码的潜在攻击者发出请求 | 必需 |
响应说明可参考授权码中的。