OAuth2协议-授权模式详解


什么是 OAuth 2.0

OAuth 2.0 是一个行业的标准授权协议。
OAuth 2.0 专注于简化客户端开发人员,同时为 Web 应用程序,桌面应用程序,手机和客厅设备提供特定的授权流程。
下面我们对OAuth2的4中授权模式进行详细的介绍。主要参考资料RFC6749

名词解释

在详细讲解OAuth 2.0之前,需要了解几个专用名词。

(1)Third-party application:第三方应用程序,本文中又称”客户端”(client),使用资源所有者的授权代表资源所有者发起对受保护资源的请求的应用程序。如:web网站,移动应用等。
(2)HTTP service:HTTP服务提供商,本文中简称”服务提供商”。
(3)Resource Owner:资源所有者,本文中又称”用户”(user)。
(4)User Agent:用户代理,帮助资源所有者与客户端沟通的工具,一般为 web 浏览器,移动 APP 等。
(5)Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
(6)Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。

OAuth思路

OAuth在”客户端”与”服务提供商”之间,设置了一个授权层(authorization layer)。”客户端”不能直接登录”服务提供商”,只能登录授权层,以此将用户与客户端区分开来。
“客户端”登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。
“客户端”登录授权层以后,”服务提供商”根据令牌的权限范围和有效期,向”客户端”开放用户储存的资料。
例如:假如我想要在 coding.net 这个网站上用 github.com 的账号登录。那么 coding 相对于 github 就是一个客户端。而我们用什么操作的呢?浏览器,这就是一个用户代理。当从 github 的授权服务器获得 token 后,coding 是需要请求 github 账号信息的,从哪请求?从 github 的资源服务器。

协议流程

+--------+                               +---------------+
  |        |--(A)- Authorization Request ->|   Resource    |
  |        |                               |     Owner     |
  |        |<-(B)-- Authorization Grant ---|               |
  |        |                               +---------------+
  |        |
  |        |                               +---------------+
  |        |--(C)-- Authorization Grant -->| Authorization |
  | Client |                               |     Server    |
  |        |<-(D)----- Access Token -------|               |
  |        |                               +---------------+
  |        |
  |        |                               +---------------+
  |        |--(E)----- Access Token ------>|    Resource   |
  |        |                               |     Server    |
  |        |<-(F)--- Protected Resource ---|               |
  +--------+                               +---------------+
  • (A) Client 请求 Resource Owner 的授权。授权请求可以直接向 Resource Owner 请求,也可以通过 Authorization Server 间接的进行。
  • (B) Client 获得授权许可。
  • (C) Client 向 Authorization Server 请求访问令牌。
  • (D) Authorization Server 验证授权许可,如果有效则颁发访问令牌。
  • (E) Client 通过访问令牌从 Resource Server 请求受保护资源。
  • (F) Resource Server 验证访问令牌,有效则响应请求。
    不难看出来,上面六个步骤之中,B是关键,即用户怎样才能给于客户端授权。有了这个授权以后,客户端就可以获取令牌,进而凭令牌获取资源。
    下面一一讲解客户端获取授权的四种模式。

    授权模式

    授权

    一个客户端想要获得授权,就需要先到服务商那注册你的应用。一般需要你提供下面这些信息:
    • 应用名称
    • 应用网站
    • 重定向 URI 或回调 URL(redirect_uri),重定向 URI 是服务商在用户授权(或拒绝)应用程序之后重定向用户的地址,因此也是用于处理授权代码或访问令牌的应用程序的一部分。
    • 客户端标识 client_id
    • 客户端密钥 client_secret
      client_id 用来表识客户端(公开),client_secret 用来验证客户端身份(保密)。

      模式

      客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。
    • 授权码模式(authorization code):服务器与客户端配合使用。
    • 隐式模式(implicit):用于移动应用程序或 Web 应用程序(在用户设备上运行的应用程序)。
    • 密码模式(resource owner password credentials):资源所有者和客户端之间具有高度信任时(例如,客户端是设备的操作系统的一部分,或者是一个高度特权应用程序),以及当其他授权许可类型(例如授权码)不可用时被使用。
    • 客户端模式(client credentials):当客户端代表自己表演(客户端也是资源所有者)或者基于与授权服务器事先商定的授权请求对受保护资源的访问权限时,客户端凭据被用作为授权许可。

      授权码模式

      授权码模式(authorization code)是功能最完整、流程最严密的授权模式。
      它的特点就是通过客户端的后台服务器,与”服务提供商”的认证服务器进行互动。
        +----------+
        | 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 -------------------'

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
具体说明,这里以 coding 和 github 为例。当我想在 coding 上通过 github 账号登录时:

1.Get请求

https://github.com/login/oauth/authorize?
response_type=code&
client_id=xxx&
redirect_uri=https://coding.net/api/oauth/github/callback&
scope=user:email

字段 描述
response_type 必须,固定为 code,表示这是一个授权码请求。
client_id 必须,在 github 注册获得的客户端 ID。
redirect_uri 可选,通过客户端注册的重定向 URI(一般要求且与注册时一致)。
scope 可选,请求资源范围,多个空格隔开。
state 必须,可选(推荐),如果存在,原样返回给客户端。
返回值
字段 描述
:— :—:
code 必须。授权码
state 如果出现在请求中,必须包含。
#### 2.Post请求获取令牌token,当获取到授权码 code 后,客户端需要用它获取访问令牌:

https://github.com/login/oauth/access_token?
client_id=xxx&
client_secret=xxxx&
grant_type=authorization_code&
code=xxx&
redirect_uri=https://coding.net/api/oauth/github/callback

字段 描述
client_id 必须,客户端标识。
client_secret 必须,客户端密钥。
grant_type 必须,固定为 authorization_code/refresh_token。
code 必须,上一步获取到的授权码。
redirect_uri 必须,完成授权后的回调地址,与注册时一致。
返回值:

{
“access_token”:”a14afef0f66fcffce3e0fcd2e34f6ff4”,
“token_type”:”bearer”,
“expires_in”:3920,
“refresh_token”:”5d633d136b6d56a41829b73a424803ec”
}

字段 描述
access_token 这个就是最终获取到的令牌。
token_type 令牌类型,常见有 bearer/mac/token(可自定义)。
expires_in 失效时间。
refresh_token 刷新令牌,用来刷新 access_token。
获取资源服务器资源,拿着 access_token 就可以获取账号的相关信息了。

隐式模式

该方式一般用于移动客户端或网页客户端。隐式授权类似于授权码授权,不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了”授权码”这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。

      +----------+
      | 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.

它的步骤如下:

(A)客户端将用户导向认证服务器。

(B)用户决定是否给于客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端指定的”重定向URI”,并在URI的Hash部分包含了访问令牌。

(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。

(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。

(F)浏览器执行上一步获得的脚本,提取出令牌。

(G)浏览器将令牌发给客户端。
同样以 coding 和 github 为例:

https://github.com/login/oauth/authorize?
response_type=token&
client_id=xxx&
redirect_uri=https://coding.net/api/oauth/github/callback&
scope=user:email

字段 描述
response_type 必须。固定为 token
client_id 必须。当客户端被注册时,有授权服务器分配的客户端标识。
redirect_uri 可选。由客户端注册的重定向URI。
scope 可选。请求可能的作用域。
state 可选(推荐)。任何需要被传递到客户端请求的URI客户端的状态。
返回值:
字段 描述
:— :—
access_token 必须。授权服务器分配的访问令牌。
token_type 必须。令牌类型。该值大小写不敏感。
redirect_uri 可选。表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
scope 可选。表示权限范围,如果与客户端申请的范围一致,此项可省略。
state 如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。
通过上面的步骤拿到token后就可以进行后续自己想要的操作了。
### 密码模式
密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向”服务商提供商”索要授权。

在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。

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

它的步骤如下:

(A)用户向客户端提供用户名和密码。

(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。

(C)认证服务器确认无误后,向客户端提供访问令牌。

B步骤中,客户端发出的POST请求

https://oauth.example.com/token?grant_type=password&username=USERNAME&password=PASSWORD&client_id=CLIENT_ID

字段 描述
grant_type 必须。固定为 password。
username 必须。UTF-8 编码的资源拥有者用户名。
password 必须。UTF-8 编码的资源拥有者密码。
scope 可选。授权范围。
返回值:

{
“access_token” : “…”,
“token_type” : “…”,
“expires_in” : “…”,
“refresh_token” : “…”,
}

如果授权服务器验证成功,那么将直接返回令牌 token,改客户端已被授权。

客户端模式

客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向”服务提供商”进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求”服务提供商”提供服务,其实不存在授权问题。
这种模式只需要提供 client_id 和 client_secret 即可获取授权。一般用于后端 API 的相关操作。

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

它的步骤如下:

(A)客户端向认证服务器进行身份认证,并要求一个访问令牌。

(B)认证服务器确认无误后,向客户端提供访问令牌。

POST 请求 客户端凭证

https://oauth.example.com/token?grant_type=client_credentials&username=USERNAME&password=PASSWORD&client_id=CLIENT_ID

字段 描述
grant_type 必须。固定为 client_credentials。
scope 可选。授权的作用域。
返回值:

{
“access_token” : “…”,
“token_type” : “…”,
“expires_in” : “…”,
}

刷新更新(POST请求

   +--------+                                           +---------------+
   |        |--(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        +---------------+

上面获取的access_token是有时效性的,再token失效前,我们需要去刷新token

https://github.com/login/oauth/access_token?
client_id=xxx&
client_secret=xxxx&
redirect_uri=https://coding.net/api/oauth/github/callback&
grant_type=refresh_token&
refresh_token=xxx

字段 描述
client_id 必须
client_secret 必须
redirect_uri 必须
grant_type 必须,固定为 refresh_token
refresh_token 必须,上面获取到的 refresh_token

注意refresh_token 只有在 access_token 过期时才能使用,并且只能使用一次。当换取到的 access_token 再次过期时,使用新的 refresh_token 来换取 access_token

友情链接:
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
https://deepzz.com/post/what-is-oauth2-protocol.html


文章作者: Niww
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Niww !
 上一篇
Keycloak配置 Keycloak配置
Keycloak切换MySQL数据库Keycloak默认使用嵌入式关系数据库H2,真实生产环境肯定是不符合要求的,在其官方文档中有这么一段话: We highly recommend that you replace it with a
下一篇 
OAuth2-类比讲解什么是OAuth2 OAuth2-类比讲解什么是OAuth2
OAuth2简介OAuth2.0是目前最流行的授权机制,用来授权第三方应用,获取用户数据。官方的定义: An open protocol to allow secure authorization in a simple and stan
2020-01-13
  目录