理解 OAuth2 協議

文章首發於:github.com/USTB-musion…html

寫在前面

昨天在組內分享了第三方登陸與單點登陸,其中着重分享了第三方登陸當中的oAuth2協議,在這裏記錄整理一下。oAuth協議是一個受權的開放網絡標準,主要是用來解決第三方登陸的,即所謂第三方登陸,實際上就是oAuth的受權。前端

什麼是第三方登陸

不少網站登陸時,容許使用第三方網站的身份來進行登陸,這稱爲「第三方登陸」。好比知乎和慕課網等,可使用微信,QQ,或微博來進行登陸。一個網站想接入第三方登陸,須要用到oAuth這個協議。git

什麼是oAuth

oAuth是一個關於受權的開放網絡標準,用來受權第三方應用,獲取用戶的數據。其最終的目的是爲了給第三方應用頒發一個有時效性的令牌access_token,第三方應用根據這個access_token就能夠去獲取用戶的相關資源,如頭像,暱稱,email這些信息。如今你們用的基本是2.0的版本。github

oAuth2.0的這個協議是從RFC 6749這篇文章當中提出來的,若是想了解更全面的信息,能夠點擊👆這篇文章進行了解。下面來介紹一下oAuth2的角色和流程。web

協議流程

在詳細介紹oAuth2協議流程以前,先來簡單瞭解幾個角色,方便後續的理解。docker

  • Resource Owner,資源全部者,由於是請求用戶的頭像和暱稱的一些信息,因此資源的全部者通常指用戶本身。
  • Client,客戶端,如web網站,app等
  • Resource Server,資源服務器,託管受保護資源的服務器
  • Authorization Server,受權服務器,通常和資源服務器是同一家公司的應用,主要是用來處理受權,給客戶端頒發令牌
  • User-agent,用戶代理,通常爲web瀏覽器,在手機上就是app

瞭解了上面這些角色以後,來看下oAuth2.0的運行流程是怎麼樣的。json

+--------+                               +---------------+
 |        |--(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)發送一個受權請求後端

(B). 用戶贊成給客戶端(Client)受權瀏覽器

(C). 客戶端使用剛纔的受權去向認證服務器(Authorization Server)認證bash

(D). 認證服務器認證經過後,會給客戶端發放令牌(Access Token)

(E). 客戶端拿着令牌(Access Token),去向資源服務器(Resource Server)申請獲取資源

(F). 資源服務器確認令牌以後,給客戶端返回受保護的資源(Protected Resource)

受權方式

在oAuth2當中,定義了四種受權方式,針對不一樣的業務場景:

  • 受權碼模式(authorization code): 流程最完整和嚴密的一種受權方式,服務器和客戶端配合使用,主要是針對web服務器的狀況採用
  • 簡化模式(implicit):主要用於移動應用程序或純前端的web應用程序,主要是針對沒有web服務器的狀況採用
  • 密碼模式(resource owner password credentials):不推薦,用戶須要向客戶端提供本身的帳號和密碼,若是客戶端是自家應用的話,也是能夠的
  • 客戶端模式(client credentials):客戶端以本身的名義,而不是用戶的名義,向「服務提供商」進行認證,如微信公衆號以此access_token來拉取全部已關注用戶的信息,docker到dockerhub拉取鏡像等

受權碼模式

+----------+
 | 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)
複製代碼

Note: The lines illustrating steps (A), (B), and (C) are broken into two parts as they pass through the user-agent.

受權碼模式如上圖所示,這種流程是功能最完整,流程也是最嚴密的受權方式,適用於那些有後端的web應用。它的特色是經過客戶端的後臺服務器和服務商的認證服務器進行通信。它的流程以下,若是我想使用github來接入第三方登陸:

(A). 用戶(Resource Owner)在用戶代理(User-Agent,如web瀏覽器,app)上選擇了第三方應用(如github)來進行登陸,會重定向到github的受權端點:

https://github.com/login/oauth/authorize?
   response_type=code&
   client_id=your_code&
   redirect_uri=重定向的url&
   scope=read&
   state=uuid
複製代碼
字段 描述
response_type 必須,在受權碼模式中固定爲code
client_id 必須,惟一標識了客戶端,在github註冊時得到的客戶端ID
redirect_url 客戶端在github註冊的重定向url,用戶贊成或拒絕的時候都會跳轉到這個重定向url
scope 可選,請求資源範圍,若有多項,使用多個空格隔開
state 推薦,客戶端生成的隨機數,資源服務器會原樣返回,防止CSRF的攻擊

(B). 頁面跳轉後,github會要求用戶登陸,而後詢問是否給予客戶端受權,用戶點擊贊成。

(C). 而後github就會將受權碼(Authorization Code)返回給redirect_uri(重定向uri)。

redirect_uri?code=xxxxxxx
複製代碼
字段 描述
code 必須,受權碼
state 防止CSRF攻擊的參數

(D). 客戶端(Client)在經過在URL中取出受權碼以後,就能夠在後端向github請求令牌

https://github.com/login/oauth/access_token?
  client_id=your_code&
  client_secret=your_secret&
  grant_type=authorization_code&
  code=取出的code&
  redirect_uri=重定向的url
複製代碼
字段 描述
client_id 必須,客戶端在github註冊的惟一標識
client_secret 必須,客戶端在github註冊時返回的密鑰
grant_type 必須,authorization_code/refresh_code
code 必須,上一步中取出的受權碼
redirect_uri 必須,完成受權以後的回調地址,與在github註冊時的一致

(E). github給redirect_uri指定的地址返回AccessToken,經過JSON格式返回

{
  "access_token":"xxxxxxx",
  "token_type":"bearer",
  "expires_in":3600,
  "refresh_token":"xxxxxxx"
}
複製代碼

客戶端就能夠在後端取到access_token,在這段json中,還返回了一個refresh_token,這個refresh_token表示用於訪問下一次的更新令牌,refresh_token的時效性比access_token長,當access_token過時時,可使用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.

主要是B這個步驟,頁面跳轉到github網站,用戶贊成給予客戶端受權。github就會把令牌做爲URL參數,跳轉回到redirect_uri的這個回調地址。

回調地址#token=xxxxxx
複製代碼

注意,令牌的位置是 URL 錨點(fragment),而不是查詢字符串(querystring),這是由於 OAuth 2.0 容許跳轉網址是 HTTP 協議,所以存在"中間人攻擊"的風險,而瀏覽器跳轉時,錨點不會發到服務器,就減小了泄漏令牌的風險。

密碼模式

若是你高度信任某個應用,RFC 6749 也容許用戶把用戶名和密碼,直接告訴該應用。該應用就使用你的密碼,申請令牌,這種方式稱爲"密碼式"(password)。

+----------+
 | 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: Resource Owner Password Credentials Flow
複製代碼

密碼模式就是用戶向客戶端提供本身的帳號和密碼,客戶端使用這些信息去向咱們的服務提供商去索要一個受權。

客戶端模式

客戶端以本身的名義,而不是用戶的名義,向「服務提供商」進行認證,如微信公衆號以此access_token來拉取全部已關注用戶的信息,docker到dockerhub拉取鏡像等。

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

                 Figure 6: Client Credentials Flow
複製代碼

客戶端模式,顧名思義就是指客戶端以本身的名義而不是用戶的名義去向服務的提供商去作一個認證,嚴格來講,這種模式並非 oAuth 框架要解決的問題,在這種客戶端模式下呢,它是直接經過客戶端的密鑰和id去獲取一個access_token的,不須要用戶去參與。

單點登陸

那關於 oAuth2 的理解呢,大概介紹這麼多的內容。oAuth 協議主要是用來解決第三方登陸的,但聊到不一樣場景下的登陸方案時,除了第三方登陸以外,還有一個概念,就是單點登陸。

單點登陸就是在多個系統中,用戶只需登陸一次,各個系統就能夠感知該用戶已經登陸。好比說你登陸了天貓,淘寶也會自動登陸。簡單地理解,單點登陸就是這樣,它經過將兩個或多個產品中的用戶登陸邏輯抽離出來,經過只輸入一次用戶名和密碼,就能夠達到同時登陸多個產品的效果。

單點登陸實現方案

第一種是同一父域下的單點登陸,好比說hr.oa.com,km.oa.com,fuli.oa.com,那這種狀況就能夠經過將domain屬性設置爲二級域名oa.com來共享cookie,而後服務端經過共享session就能夠實現單點登陸。除了共享session以外固然也能夠用JWT這種方式進行實現。

那第二種就是針對不一樣域下的單點登陸,好比說淘寶和天貓,它的二級域名是不相同的。這種狀況,就要解決cookie不共享的問題。如今主流的方案就是使用cas來實現。

總結

來簡單總結一下,針對不一樣業務場景下登陸的主流解決方案,第一種是針對同一公司,同一父域下的單點登陸解決方案,這種狀況由於cookie是同父域下的,設置cookie的domain屬性能夠實現cookie共享。而後服務端session共享就能夠實現單點登陸。但還有一種這種方式解決方案是JWT。JWT就是json web token。實際上就是一個字符串,由頭部,載荷和簽名三部分組成。

第二種是針對同一公司,可是不一樣域下的單點登陸解決方案,好比說淘寶和天貓的單點登陸,那這種方式的主流解決方案是CAS。

那第三種就是不一樣公司,不一樣域下的就使用第三方登陸功能實現。如第三方網站須要接入微信登陸,QQ登陸,微博登陸等,那第三方登陸功能的實現呢,就用到剛纔介紹的 oAuth2.0 的協議。

參考文章

OAuth 2.0 的四種方式

相關文章
相關標籤/搜索