天涯旅店

OAuth 2.0 介绍和接口标准

OAuth 2.0 简介


OAuth 2.0 的设计背景,在于允许用户在不告知第三方自己的帐号密码情况下,通过授权方式,让第三方服务可以获取自己的资源信息。协议的详细信息,可以参考 RFC 6749 , RFC 6749 中文

OAuth 2.0 相对于 1.0 版本在授权模式上做了更多的细化,已定义的授权模式分为四种:

  • 授权码模式(Authorization Code Grant)
  • 隐式授权模式(Implicit Grant)
  • 资源所有者密码凭证模式(Resource Owner Password Credentials Grant)
  • 以及客户端凭证模式(Client Credentials Grant)

简单说明 OAuth2 中最经典的 Authorization Code Grant 模式,流程如下:

https://cdn.sekiro.top/OAuth-1.png

流程图中,包含四个角色。

  • ResourceOwner 为资源所有者,即为用户
  • User-Agent 为浏览器
  • AuthorizationServer 为认证服务器,可以理解为用户资源托管方,比如企业微信服务端
  • Client 为第三方服务

调用流程为:

  1. 用户访问第三方服务,第三方服务通过构造 OAuth 2.0 链接(参数包括当前第三方服务的身份 ID,以及重定向 URI),将用户引导到认证服务器的授权页。
  2. 用户查看获取授权的第三方信息,以及需要被授权的资源,选择是否同意授权。
  3. 若用户同意授权,则认证服务器将用户重定向到第一步指定的重定向 URI,同时附上一个授权码。
  4. 第三方服务收到授权码,带上授权码来源的重定向 URI,向认证服务器申请凭证。
  5. 认证服务器检查授权码和重定向 URI 的有效性,通过后颁发 AccessToken(调用凭证)。

4 与 5 的调用为后台调用,不通过浏览器进行。

OAuth 2.0 接入流程标准


测试环境地址:https://uis-sit.cmsk1979.com/security

生产环境地址:

授权码授权模式(Authorization Code Grant)

授权码模式在整个授权流程上与 1.0 版本最贴近,但是整个流程还是要简化了许多,也是 OAuth 2.0 中最标准,应用最广泛的授权模式。

这类授权模式非常适合于具备服务端的应用,当然现在大多数 APP 都有自己的服务端,所以大部分 APP 的 OAuth 授权都可以采取授权码模式,下图为授权码各个角色之间的交互时序(这里让用户直接参与其中,省略了用户代理):

https://cdn.sekiro.top/OAuth-2.png

整个授权流程说明如下(具体参数释义见下文):

  1. 客户端携带 client_id, scope, redirect_uri, state 等信息引导用户请求授权服务器的授权端点下发 code。
  2. 授权服务器验证客户端身份,验证通过则询问用户是否同意授权(此时会跳转到用户能够直观看到的授权页面,等待用户点击确认授权)。
  3. 假设用户同意授权,此时授权服务器会将 code 和 state(如果客户端传递了该参数)拼接在 redirect_uri 后面,以 302 形式下发 code。
  4. 客户端携带 code, redirect_uri, 以及 client_secret 请求授权服务器的令牌端点下发 access_token(这一步实际上中间经过了客户端的服务器,除了 code,其它参数都是在应用服务器端添加,下文会细讲)授权服务器验证客户端身份,同时验证 code,以及 redirect_uri 是否与请求 code 时相同。验证通过后下发 access_token,并选择性下发 refresh_token。

获取授权码

授权码是授权流程的一个中间临时凭证,是对用户确认授权这一操作的一个暂时性的证书,其生命周期一般较短,协议建议最大不要超过 10 分钟,在这一有效时间周期内,客户端可以凭借该暂时性证书去授权服务器换取访问令牌。

请求参数说明:

名称是否必须描述信息
response_type必须对于授权码模式 response_type=code
client_id必须客户端ID,用于标识一个客户端,等同于 appId,在注册应用时生成
redirect_uri可选授权回调地址,传输的回调地址必须和授权服务保存的回调地址一致(参数部分可不一致)
scope可选权限范围,用于对客户端的权限进行控制,如果客户端没有传递该参数,那么服务器则以该应用的所有权限代替
state推荐用于维持请求和回调过程中的状态,防止 CSRF 攻击,服务器不对该参数做任何处理,如果客户端携带了该参数,则服务器在响应时原封不动的返回

请求参数示例:

GET /oauth/authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https://example.com/oauth HTTP/1.1
Host: example.com

客户端携带上述参数请求授权服务器的令牌端点,授权服务器会验证客户端的身份以及相关参数,并在确认用户登录的前提下弹出确认授权页询问用户是否授权。
如果用户同意授权,则会将授权码(code)和 state 信息(如果客户端传递了该参数)添加到回调地址后面,以 302 的形式下发。

成功响应参数说明:

名称描述信息
code授权码,授权码代表用户确认授权的暂时性凭证,只能使用一次,推荐最大生命周期不超过10分钟
state如果客户端传递了该参数,则会原封不动返回

成功响应示例:

HTTP/1.1 302 Found
Location: https://example.com/oauth?code=SplxlOB&state=xyz

下发访问令牌

授权服务器的授权端点在以 302 形式下发 code 之后,用户 User-Agent,比如浏览器,将携带对应的 code 回调请求用户指定的 redirect_url,这个地址应该能够保证请求打到应用服务器的对应接口。该接口可以由此拿到 code,并附加相应参数请求授权服务器的令牌端点,授权端点验证 code 和相关参数,验证通过则下发 access_token。

请求参数说明:

名称是否必须描述信息
grant_type必须对于授权码模式 grant_type=authorization_code
code必须上一步获取的授权码
redirect_uri必须授权回调地址,传输的回调地址必须和授权服务保存的回调地址一致
client_id必须客户端 ID,用于标识一个客户端,等同于 appId,在注册应用时生成

如果在注册应用时有下发客户端凭证信息(client_secret),那么客户端必须携带 Authorization 信息以让授权服务器验证客户端的有效性。针对客户端凭证,不能将其传递到客户端,客户端无法保证凭证的安全,凭证应该始终留在应用的服务器端,当下发 code 回调请求到应用服务器时,在服务器端携带上凭证再次请求下发令牌。

请求参数示例:

POST /oauth/token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOB&redirect_uri=https://client.example.com/oauth&client_id=client_test

添加 Authorization 示例:

import org.apache.http.client.methods.HttpPost;

import java.util.Base64;

public class Test {

    private void addHeader() {
        //向授权服务器申请时提供的client_id
        String userName = "09853e59-e240-450f-b732-001af3207149";
        //向授权服务器申请时提供的client_secret
        String password = "c7492ba5-f749-480f-bec5-55ab6bdc7161";
        HttpPost httpPost = new HttpPost("http://127.0.0.1:8080/oauth/token");
        //添加http头信息
        httpPost.addHeader("Authorization", "Basic " + Base64.getUrlEncoder()
                .encodeToString((userName + ":" + password).getBytes()));
    }
}

授权服务器需要验证客户端的有效性,以及是否与之前请求授权码的客户端是同一个(请求授权时的信息可以记录在 code,或以 code 为 key 建立缓存),授权服务器还要保证 code 处于生命周期内(推荐 10 分钟内有效),且只能被使用一次。

授权服务器验证通过之后,生成 access_token,并选择性下发 refresh_token,OAuth 2.0 协议明确了 token 的下发策略,对于生成策略没有做太多说明。

成功响应参数说明:

名称描述信息
access_token访问令牌
token_type访问令牌类型,比如 bearer,mac 等等
expires_in访问令牌的生命周期,以秒为单位,表示令牌下发后多久时间过期,如果没有指定该项,则使用默认值
refresh_token刷新令牌,选择性下发
scope权限范围,如果最终下发的访问令牌对应的权限范围与实际应用指定的不一致,则必须在下发访问令牌时用该参数指定说明
openid授权用户唯一标识

最后访问令牌以 JSON 格式响应,并要求指定响应头部 Cache-Control: no-storePragma: no-cache

成功响应示例:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
    "access_token":"2YotnFZFEjr1zCsicMWpAA",
    "token_type":"bearer",
    "expires_in":7200,
    "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
    "scope":"all",
    "openid":"123456"
}

刷新令牌

access_token 是调用授权关系接口的调用凭证,由于 access_token 有效期(目前为 2 个小时)较短,当 access_token 超时后,可以使用 refresh_token 进行刷新,refresh_token 拥有较长的有效期(30 天),当 refresh_token 失效的后,需要用户重新授权。

另外,请求刷新令牌后,原令牌失效。

请求参数说明:

名称是否必须描述信息
grant_type必须刷新令牌时 grant_type=refresh_toekn
refresh_toekn必须返回的refresh_token
client_id必须客户端id

请求参数示例,注意这里和获取令牌一样,如果有下发 client_secret,必须附带 Authorization 信息:

POST /oauth/token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_toekn&refresh_toekn=tGzv3JOkF0XG5Qx2TlKWIA&client_id=client_test

成功响应参数说明:

名称描述信息
access_token访问令牌
token_type访问令牌类型,比如 bearer,mac 等等
expires_in访问令牌的生命周期,以秒为单位,表示令牌下发后多久时间过期,如果没有指定该项,则使用默认值
refresh_token刷新令牌,选择性下发
scope权限范围,如果最终下发的访问令牌对应的权限范围与实际应用指定的不一致,则必须在下发访问令牌时用该参数指定说明
openid授权用户唯一标识

成功响应示例:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
    "access_token":"2YotnFZFEjr1zCsicMWpAA",
    "token_type":"bearer",
    "expires_in":7200,
    "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
    "scope":"all",
    "openid":"123456"
}

资源所有者密码凭证授权模式(Resource Owner Password Credentials Grant)

用户高度信任当前应用,也可以给出自己的用户名/密码,快速获取令牌,本系统不推荐使用这种方式。

下发访问令牌

直接提供用户名密码,获取令牌。

请求参数说明:

名称是否必须描述信息
grant_type必须对于授权码模式 grant_type=password
username必须用户名
password必须用户的密码
client_id必须客户端ID,用于标识一个客户端,等同于appId,在注册应用时生成
scope可选权限范围,用于对客户端的权限进行控制,如果客户端没有传递该参数,那么服务器则以该应用的所有权限代替

如果有下发 client_secret,必须附带 Authorization 信息。

请求参数示例:

POST /oauth/token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=abcd&password=123456&client_id=client_test

成功响应参数说明:

名称描述信息
access_token访问令牌
token_type访问令牌类型,比如 bearer,mac 等等
expires_in访问令牌的生命周期,以秒为单位,表示令牌下发后多久时间过期,如果没有指定该项,则使用默认值
refresh_token刷新令牌,选择性下发
scope权限范围,如果最终下发的访问令牌对应的权限范围与实际应用指定的不一致,则必须在下发访问令牌时用该参数指定说明
openid授权用户唯一标识

最后访问令牌以 JSON 格式响应,并要求指定响应头部 Cache-Control: no-storePragma: no-cache

成功响应示例:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
    "access_token":"2YotnFZFEjr1zCsicMWpAA",
    "token_type":"bearer",
    "expires_in":7200,
    "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
    "scope":"all",
    "openid":"123456"
}

刷新令牌

同授权码模式刷新令牌。

隐式授权模式(Implicit Grant)

略。(本系统不支持此类授权模式)

客户端凭证授权模式(Client Credentials Grant)

略。(本系统不支持此类授权模式)

发表回复

textsms
account_circle
email

天涯旅店

OAuth 2.0 介绍和接口标准
OAuth 2.0 简介 OAuth 2.0 的设计背景,在于允许用户在不告知第三方自己的帐号密码情况下,通过授权方式,让第三方服务可以获取自己的资源信息。协议的详细信息,可以参考 RFC 674…
扫描二维码继续阅读
2020-12-06