OAuth 2.0 綜述

OAuth 2.0 rfc6749 規範
OAuth 2.0 rfc6749 規範-帶目錄,閱讀 RFC 文檔的 工具
OAuth 官網html

OAuth 是受權(authorization)框架,描述了系統中不一樣角色(服務提供商、用戶、第三方應用)之間怎麼實現交互。目前普遍使用的是 2.0 版本。web

常見的應用場景(估計全部人都見過):受權登陸註冊。好比微信有不少用戶,且微信提供了 OAuth2 接口。在個人網站上,爲了簡化用戶註冊流程,對接了微信提供的這個接口,而且在註冊頁面提供一個「經過微信登陸」的連接或二維碼,用戶點擊後會跳轉到微信的受權登陸頁面(或者微信掃碼進入受權頁面),微信在用戶受權後會將這個用戶的基本信息發到個人網站,完成註冊。註冊後,每次均可以經過微信受權登陸了。json

若是須要作微信這樣的平臺型應用,爲不少合做夥伴提供受權認證服務,能夠考慮搭建本身的 OAuth 2.0 系統。可是要明白,這是挺複雜的一個系統。對於大部分應用開發人員,須要掌握的是如何調用其餘平臺的接口。瀏覽器

OAuth2 核心

角色

OAuth2 提供了 4 種角色:安全

  • Resource Owner:資源擁有者,例如一個微信用戶
  • Resource Server:資源服務器,例如微信用戶的基本信息所在的服務器,能夠跟受權服務器在同一臺服務器上
  • Client:客戶端(第三方應用,即資源使用者,也叫 Third-party application),例如個人網站須要使用微信用戶的帳號登陸,我就是資源使用者
  • Authorization Server:受權服務器,管理 Resource Owner,Client 和 Resource Server 三者的關係

另外的經常使用名詞有:bash

  • HTTP service:HTTP 服務的提供商,例如微信、GitHub。
  • User-Agent:用戶代理,通常指瀏覽器。

Token 類型

token 都是字符串,受權服務器須要保存全部的 token 及對應的受權信息,以便校驗請求。服務器

access token 訪問令牌

用於訪問受保護資源的憑證。這個憑證表示特定訪問範圍和可用時間,由資源全部者受權,並由資源服務器和受權服務器執行,併發給客戶端。微信

對資源的訪問,須要 access token。併發

refresh token 刷新令牌

用於再次獲取 access token 的憑證。這是可選項,能夠自行決定是否讓受權服務器發佈 refresh token。若是發佈 refresh token,會跟 access token 一同發出。refresh token 能夠在 access token 失效後獲取新的 access token,也能夠獲取更多一個 access token。app

refresh token 只會跟受權服務器交互,不會發送到資源服務器。

Client 類型

Client 有兩種類型:

  • 公開的:密碼會發給 Client,因此 Client 可能會泄露密碼。這個過程當中密碼傳輸到終端設備,例如瀏覽器,APP。
  • 私有的:密碼不會發給 Client,因此 Client 不會泄露密碼。

客戶端配置

  • Web 應用:各個網站的微信受權登陸就是典型的 Web 應用
  • User Agent(用戶代理)應用:對於瀏覽器上運行的 JavaScript 應用,瀏覽器就是用戶代理。用戶代理應用能夠保存在 web 服務器上,但應用程序只運行一次下載的用戶代理。
  • 原生應用:沒法保證 secret 的安全。

Access Token 的類型

詳細差別能夠參考 這裏

bearer 類型

參考 RFC6750

經過在請求中簡單地包含訪問令牌字符串來使用:

GET /resource/1 HTTP/1.1
Host: example.com
Authorization: Bearer mF_9.B5f-4.1JqM

mac 類型

參考 OAuth-HTTP-MAC

經過發出消息認證代碼(MAC)密鑰以及用於簽署 HTTP 請求的某些組件的訪問令牌來使用:

GET /resource/1 HTTP/1.1
Host: example.com
Authorization: MAC id="h480djs93hd8",
                   nonce="274312:dj83hs9s",
                   mac="kDZvddkndxvhGRXZhvuDjEWhGeE="

OAuth 協議的抽象工做流程

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

                     Figure 1: 抽象的協議流程

詳細步驟以下:

  1. (A) 客戶端向資源擁有者請求受權。受權請求能夠直接發送到資源全部者(如上圖所示),或最好間接經由受權服務器做爲中介。
  2. (B) 用戶向客戶端受權。這是表明了資源全部者受權的一個憑證。權限授予類型取決於客戶端請求時受權使用的方法和受權服務器支持的類型。
  3. (C) 客戶端經過向受權服務器進行認證並提交上一步得到的受權來請求令牌 access token。
  4. (D) 受權服務器驗證客戶端,並驗證客戶端提交的受權,若是有效,則發出令牌 access token。
  5. (E) 客戶端向資源服務器請求受限資源,並經過提交 access token 來驗證權限。
  6. (F) 資源服務器驗證 access token,若是有效,則提供資源。

在步驟 B 中,客戶端能夠得到用戶的受權。有四種受權模式。

受權模式

第三方應用必須獲得用戶受權(authorization grant)後才能得到訪問令牌(access token)。OAuth2 支持四種受權模式:

  • Authorization Code:受權碼模式,私有和公共的 Client 均可以經過 authorization code 來獲取 access token。用戶經過重定向 URL 返回到 Client 後,Client 的應用程序將從 URL 獲取受權碼並使用它來請求 access token。
  • Implicit:隱式受權模式(簡化模式),是供公開的 Client 使用的簡化流程,其中 access token 在沒有受權碼交換的狀況下當即返回。一般不推薦使用隱式流(而且一些服務器徹底禁止該流)。 建議公開的 Client 使用受權代碼流而不使用客戶端密鑰。
  • Password:密碼模式,經過用戶憑證(密碼)來獲取 access token。此時 Client 須要用戶輸入密碼,因此不該該被第三方客戶使用。 在此流程中,用戶的用戶名和密碼直接交換爲 access token。
  • Client Credentials:客戶端模式,由 Client 用來在用戶上下文以外獲取 access token。一般被客戶用來訪問他們本身的資源,而不是用戶的資源。Client 也是資源全部者,能夠訪問本身的資源。

另外,對於已經受權的 Client,還有兩種方式能夠再次獲取 access token:

  • Device Code:設備代碼受權模式,用於無瀏覽器或不方便輸入的設備。經過以前得到的 device code 來獲取 access token。
  • Refresh Token:在 Client 端的 access token 過時時,經過 refresh token 獲取 access token。Client 能夠經過這種方式保證 access token 的有效性(access token 的有效期一般很短),而無需與用戶進一步交互。

受權碼模式

最經常使用,微信、GitHub 等的受權登陸註冊就是這種方式。使用這種模式時,受權服務器會直接將受權發送到客戶端的後臺服務器,不須要通過用戶代理,安全可靠。

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

   注意:說明步驟(A),(B)和(C)的行在經過用戶代理時分爲兩部分。

                     Figure 3: 受權碼模式的流程

受權碼模式用於獲取訪問令牌和刷新令牌,並針對機密客戶端進行了優化。受權碼模式基於重定向,受權開始時從客戶端跳轉到受權服務器的頁面,受權完成後受權服務器後臺通知客戶端,同時跳轉回客戶端的頁面。客戶端必須可以與資源全部者的用戶代理(一般是 Web 瀏覽器)進行交互,而且可以從受權服務器接收傳入請求。

詳細步驟

  1. (A)資源全部者訪問客戶端,客戶端將其導向受權服務器。
  2. (B)受權服務器驗證資源全部者(經過用戶代理)並肯定資源全部者是否授予或拒絕客戶端的訪問請求。
  3. (C)假設資源全部者授予訪問權限,受權服務器使用先前提供的重定向 URI 將用戶代理重定向回客戶端。重定向 URI 包括受權碼和客戶端先前提供的任何本地狀態。
  4. (D)客戶端經過受權碼向受權服務器請求令牌。這一步對用戶無感知。
  5. (E)受權服務器驗證客戶端和受權碼,並確保收到的重定向 URI 與步驟 C 中用於重定向客戶端的 URI 匹配。若是有效,受權服務器將返回訪問令牌和可選的刷新令牌。

受權請求(對應步驟 A)

客戶端經過 GET 方式使用如下參數來構造請求 URI:

  • response_type:必選。受權類型。只能是「code」。
  • client_id:必選。客戶端的 ID。
  • redirect_uri:可選。重定向 URI。
  • scope:可選。申請權限的範圍。
  • state:可選。客戶端用於維護請求和回調之間狀態的不透明值。受權服務器會原封不動的返回。該參數應該用於防止跨站點請求僞造。

受權請求示例:

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

受權響應(對應步驟 C)

受權服務器的響應也是「application/x-www-form-urlencoded」格式,包含如下參數:

  • code:必選。受權碼。有效時間很短,建議設爲10分鐘(微信的是 2 小時)。一次有效,再次使用時會被受權服務器拒絕。受權碼跟客戶端 ID 和重定向 URI 綁定。
  • state:若是受權請求中包含這個參數,則必定會返回。

受權響應示例:

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

令牌申請請求(對應步驟 D)

客戶端經過 POST 方式使用「application/x-www-form-urlencoded」格式將如下參數來構造請求 URI:

  • grant_type:必選,受權模式,只能是「authorization_code」。
  • code:必選,步驟 C 中獲取的受權碼。
  • redirect_uri:必選,重定向 URI,必須與步驟 A 中的保持一致。
  • client_id:必選,客戶端 ID。

示例:

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

令牌申請響應(對應步驟 E)

受權成功後,響應包含如下參數:

  • access_token:必選,訪問令牌。
  • token_type:必選,令牌類型,該值大小寫不敏感,能夠是 bearer 類型或 mac 類型。
  • expires_in:過時時間,單位秒。若是用其餘方式設置了過時時間,則能夠省略該參數。
  • refresh_token:可選,更新令牌,用來獲取下一次的訪問令牌。
  • scope:可選,權限範圍,默認若是與客戶端申請的範圍一致

示例:

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" }

隱式受權模式(簡化模式)

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

   注意:說明步驟(A)和(B)的行在經過用戶代理時分爲兩部分。

                       Figure 4: 隱式受權模式的流程

隱式受權特色

  • 直接在瀏覽器中向認證服務器申請令牌,全部步驟在瀏覽器中完成,不須要跟客戶端服務器交互,跳過了」受權碼」這個步驟。
  • 只支持獲取訪問令牌,不支持刷新令牌,而且針對操做特定重定向 URI 的公共客戶端進行了優化。
  • 令牌對訪問者可見,且不須要客戶端認證。
  • 與受權碼模式(客戶端對受權和獲取訪問令牌發出兩個獨立請求)不一樣,客戶端會在受權請求的響應中接收訪問令牌(同一個請求中)。
  • 隱式受權不驗證客戶端身份,而且依賴於資源全部者的存在和重定向 URI 的註冊。因爲訪問令牌被編碼到重定向 URI 中,所以可能會暴露給資源全部者和同一設備上的其餘應用程序。

詳細步驟

  1. (A) 客戶端將用戶導向受權服務器,須要包含如下參數:客戶端 ID、scope、state、重定向 URI。
  2. (B) 受權服務器認證資源全部者(經過用戶代理),並判斷是否向客戶端受權。
  3. (C) 假設受權成功,受權服務器會使用上面提供的重定向 URI 跳轉回客戶端,並在 URI 的片斷中附加 access token。
  4. (D) 用戶代理(通常是瀏覽器)向資源服務器發請求,不包含上面附加 access token 的 URI 片斷。
  5. (E) 資源服務器返回網頁(一般是內嵌了腳本的 HTML 文檔),用於獲取完整的包含 access token 的重定向 URI 的。
  6. (F) 用戶代理執行上一步獲取的腳本,提取 access token。
  7. (G) 用戶代理將 access token 發送到客戶端。

受權請求

客戶端經過添加下面的參數來構造請求 URI

  • response_type:必選。受權類型。只能是「token」。
  • client_id:必選。客戶端的 ID。
  • redirect_uri:可選。重定向 URI。
  • scope:可選。申請權限的範圍。
  • state:可選。客戶端用於維護請求和回調之間狀態的不透明值。受權服務器會原封不動的返回。該參數應該用於防止跨站點請求僞造。

客戶端使用 HTTP 重定向響應或經過用戶代理可用的其餘方式將資源全部者定向到構造的 URI。示例:

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

受權服務器必須驗證 redirect_uri 參數是否和客戶端預留的參數一致。

Access Token 響應

用戶受權後,受權服務器會頒發一個 access token 併發送到客戶端,可用參數以下:

  • access_token:必選,訪問令牌。
  • token_type:必選,令牌類型,該值大小寫不敏感,能夠是 bearer 類型或 mac 類型。
  • expires_in:過時時間,單位秒。若是用其餘方式設置了過時時間,則能夠省略該參數。
  • scope:可選,權限範圍,默認若是與客戶端申請的範圍一致。
  • state:若是受權請求中包含這個參數,則必定會返回。

注意:這裏沒有 refresh token。

示例:

HTTP/1.1 302 Found
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
           &state=xyz&token_type=example&expires_in=3600

密碼模式

+----------+
     | 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: 密碼模式流程

資源全部者將用戶名和密碼發送給客戶端,而後客戶端用這些憑證獲取受權。

詳細步驟

  1. (A)資源全部者向客戶端提供用戶名和密碼。
  2. (B)客戶端將用戶名和密碼發給認證服務器,請求訪問令牌。
  3. (C)認證服務器向客戶端提供訪問令牌。

令牌申請請求(對應步驟 B)

客戶端經過 GET 方式使用如下參數來構造請求 URI:

  • grant_type:必選。受權類型。只能是「password」。
  • username:必選。資源全部者的用戶名。
  • password:必選。資源全部者的密碼。
  • scope:可選。申請權限的範圍。
  • state:可選。客戶端用於維護請求和回調之間狀態的不透明值。受權服務器會原封不動的返回。該參數應該用於防止跨站點請求僞造。

受權請求示例:

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

令牌申請響應(對應步驟 C)

受權成功後,響應包含如下參數:

  • access_token:必選,訪問令牌。
  • token_type:必選,令牌類型,該值大小寫不敏感,能夠是 bearer 類型或 mac 類型。
  • expires_in:過時時間,單位秒。若是用其餘方式設置了過時時間,則能夠省略該參數。
  • refresh_token:可選,更新令牌,用來獲取下一次的訪問令牌。
  • scope:可選,權限範圍,默認若是與客戶端申請的範圍一致

示例:

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

客戶端以本身的名義,而不是以用戶的名義,向」服務提供商」進行認證。嚴格地說,客戶端模式並不屬於OAuth框架所要解決的問題。在這種模式中,用戶直接向客戶端註冊,客戶端以本身的名義要求」服務提供商」提供服務,其實不存在受權問題。

詳細步驟

  1. (A)客戶端向認證服務器進行身份認證,並要求一個訪問令牌。
  2. (B)認證服務器確認無誤後,向客戶端提供訪問令牌。

令牌申請請求(對應步驟 A)

客戶端經過 GET 方式使用如下參數來構造請求 URI:

  • grant_type:必選。受權類型。只能是「client_credentials」。
  • scope:可選。申請權限的範圍。

受權請求示例:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

令牌申請響應(對應步驟 B)

受權成功後,響應包含如下參數:

  • access_token:必選,訪問令牌。
  • token_type:必選,令牌類型,該值大小寫不敏感,能夠是 bearer 類型或 mac 類型。
  • expires_in:過時時間,單位秒。若是用其餘方式設置了過時時間,則能夠省略該參數。
  • refresh_token:可選,更新令牌,用來獲取下一次的訪問令牌。
  • scope:可選,權限範圍,默認若是與客戶端申請的範圍一致

示例:

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" }

刷新過時的 Access Token

+--------+                                           +---------------+
  |        |--(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: 刷新過時的 Access Token

詳細步驟

  1. (A) 客戶端經過向受權服務器進行身份驗證並提交受權許可來請求 access token。
  2. (B) 受權服務器對客戶端進行身份驗證並驗證受權許可,若是有效,則發出 access token 和 refresh token。
  3. (C) 客戶端向資源服務器遞交 access token,以訪問受保護的資源。
  4. (D) 資源服務器驗證 access token,若是有效,則提供資源。
  5. (E) 重複進行上面兩個步驟,直到 access token 過時。若是客戶端知道 access token 過時,跳到第 7 步,不然繼續發出訪問資源的請求。
  6. (F) 由於 access token 已經失效了,資源服務器報錯「invalid token error」。
  7. (G) 客戶端經過向受權服務器進行身份驗證並提交 refresh token 來請求 access token。客戶端的受權請求基於客戶端類型和受權服務器的策略。
  8. (H) 受權服務器驗證客戶端並驗證 refresh token,若是有效則發出新的 access token(也能夠發出一個新的 refresh token)。

令牌申請請求(對應步驟 G)

客戶端經過 GET 方式使用如下參數來構造請求 URI:

  • grant_type:必選。受權類型。只能是「refresh_token」。
  • refresh_token:必選項。以前收到的更新令牌。
  • scope:可選。申請權限的範圍。

受權請求示例:

POST/token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
相關文章
相關標籤/搜索