【全棧修鍊】396- OAuth2 修鍊寶典

  • 2019 年 11 月 5 日
  • 筆記

一、OAuth 概念

開放授權(OAuth)是一個開放標準,允許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源(如照片,影片,聯繫人列表),而無需將用戶名和密碼提供給第三方應用。—— 維基百科

嚴格來說,OAuth2 不是一個標準協議,而是一個安全的授權框架。其詳細描述系統中不同角色,用戶,服務前端應用(如 API )以及客戶端(如網站或APP)之間如何實現相互認證

當前 OAuth 協議版本是 OAuth2.0,需要注意的是,OAuth2.0 並不向下兼容 OAuth1.0。

在生活中,比較常見的 OAuth2 的使用場景是授權登錄,並且也廣泛應用於 web、桌面應用和移動 APP 的第三方服務提供授權登錄驗證機制,以實現不同應用直接數據訪問的許可權

二、OAuth2 重點名詞介紹

在 OAuth2 標準中定義了以下四種角色:

  • 資源擁有者 (Resource Owner):

代表授權客戶端訪問本身資源資訊的用戶(User);

  • 客戶端 (Client):

代表意圖訪問受限資源的第三方應用。

  • 資源伺服器 (Resource Server):

代表託管了受保護的用戶帳號資訊的伺服器,它與認證伺服器,可以是同一台伺服器,也可以是不同的伺服器;

  • 授權伺服器 (Authorization Server):

代表驗證用戶身份然後為客戶端派發資源訪問令牌的伺服器,即服務提供商專門用來處理認證的伺服器;

三、OAuth2 運行流程

1. 流程分析

(配圖來自阮一峰大佬

大致流程概括就是:

  • (A)Authrization Request

用戶打開客戶端以後,客戶端要求用戶給予授權。

  • (B)Authorization Grant(Get)

用戶同意給予客戶端授權。

  • (C)Authorization Grant(Post)

客戶端向授權伺服器發送它自己的客戶端身份標識和上一步中獲得的授權(authorization grant),向認證伺服器申請令牌。

  • (D)Access Token(Get)

認證伺服器對客戶端進行認證以後,確認無誤,同意發放令牌(access token),授權階段至此全部結束。

  • (E)Access Token(Post && Validate)

客戶端使用令牌,向資源伺服器申請獲取資源。

  • (F)Protected Resource(Get)

資源伺服器確認令牌無誤,同意向客戶端開放資源。

理解完上面整個流程以後,我們再看看下面這張圖,能更加清晰理解 OAuth2 的整個運行流程:

(配圖來自公眾號前端修仙之路

從整個流程可以看出,在 B 步驟最為關鍵,即需要獲取到用戶對客戶端的授權(如我們在微信掃碼登錄時,點擊「確定」按鈕的步驟)。

有了這個授權以後,客戶端才能拿到令牌,進而憑令牌向資源伺服器獲取資源。

2. 案例:微信登錄

另外,微信登錄的實現流程也類似:

(配圖來自微信官方文檔

其整體流程為:

  1. 第三方發起微信授權登錄請求,微信用戶允許授權第三方應用後,微信會拉起應用或重定向到第三方網站,並且帶上授權臨時票據 code 參數;
  2. 通過 code 參數加上 AppIDAppSecret 等,通過 API 換取 access_token
  3. 通過 access_token 進行介面調用,獲取用戶基本數據資源或幫助用戶實現基本操作

3. OAuth2 優缺點

  • 優點:

適合快速開發實施,程式碼量少,API需要被不同APP使用,且每個APP使用方式也不同的情況。

  • 缺點:

學習和理解的成本比較大,並且 OAuth2 不是一個嚴格的標準協議,在實施過程中更容易出錯。

四、OAuth2 四種授權模式

通過前面描述,可以知道OAuth 的核心就是向第三方應用頒發令牌。

OAuth 2.0 規定了四種獲得令牌的流程。你可以選擇最適合自己的那一種,向第三方應用頒發令牌。即以下四種授權方式:

  • 授權碼(authorization-code)
  • 隱藏式(implicit)
  • 密碼式(password)
  • 客戶端憑證(client credentials)

注意:

不管哪一種授權方式,第三方應用申請令牌之前,都必須先到系統備案,說明自己的身份,然後會拿到兩個身份識別碼:客戶端 ID(client ID)客戶端密鑰(client secret)

這是為了防止令牌被濫用,沒有備案過的第三方應用,是不會拿到令牌的。

1. 授權碼(authorization code)

第三方應用先申請一個授權碼,然後再用該碼獲取令牌

適用於有後端的 Web 應用,授權碼通過前端傳送,令牌則是儲存在後端,而且所有與資源伺服器的通訊都在後端完成。這樣的前後端分離,可以避免令牌泄漏。

這種方式也是最常用的流程,安全性最高

整體流程

(配圖來自阮一峰大佬

步驟分析

  1. 用戶從 A 網站跳轉到 B 網站,請求用戶確認授權,以獲取授權碼,其發送鏈接示意如下:
https://b.com/oauth/authorize?       response_type=code&       client_id=CLIENT_ID&       redirect_uri=CALLBACK_URL&       scope=read

其中:

response_type 參數表示要求返回授權碼(code);

client_id 參數讓 B 知道是誰在請求;

redirect_uri 參數是 B 接受或拒絕請求後的跳轉網址;

scope 參數表示要求的授權範圍(這裡是只讀);

  1. 在 B 網站中,當用戶同意授權 A 網站,則 B 網站會攜帶授權碼,重定向到 redirect_uri 參數指定的網址,就像下面這樣:
https://a.com/callback?code=AUTHORIZATION_CODE
  1. A 網站獲取授權碼以後,在 A 網站後端中向 B 網站請求令牌:
https://b.com/oauth/token?       client_id=CLIENT_ID&       client_secret=CLIENT_SECRET&       grant_type=authorization_code&       code=AUTHORIZATION_CODE&       redirect_uri=CALLBACK_URL

client_id 參數和 client_secret 參數用來讓 B 確認 A 的身份( client_secret 參數是保密的,因此只能在後端發請求);

grant_type 參數的值是 AUTHORIZATION_CODE ,表示採用的授權方式是授權碼;

code 參數是上一步拿到的授權碼;

redirect_uri 參數是令牌頒發後的回調網址;

  1. B 網站接受請求並驗證身份,身份驗證通過後,會發放令牌。向 redirect_uri 指定的網址,發送包含令牌 access_token 欄位的JSON數據,流程完畢。

2. 隱藏式(implicit)

隱藏授權碼步驟,直接向前端發放令牌,也稱授權碼隱藏式。

整體流程

(配圖來自阮一峰大佬

步驟分析

  1. 用戶從 A 網站跳轉到 B 網站,要求授權用戶數據給 A 網站使用。
https://b.com/oauth/authorize?  response_type=token&  client_id=CLIENT_ID&  redirect_uri=CALLBACK_URL&  scope=read

response_type 參數為 token,表示要求直接返回令牌

  1. 用戶在 B 網站同意授權給 A 網站。

當用戶同意授權後,會跳轉到 redirect_uri 參數指定的重定向地址,並將令牌作為 URL 參數傳遞給 A 網站。

https://a.com/callback#token=ACCESS_TOKEN

token 參數就是令牌,A 網站因此直接在前端拿到令牌。

注意:

這裡的令牌位置是 URL 錨點(即 # 號),而不是查詢字元串,這是因為錨點不會發到伺服器,避免泄露令牌的風險。

適用場景:

由於直接傳遞令牌不安全,因此常常適用在對安全要求不高的場景,並且令牌有效期非常短,例如會話期間(session)有效,關閉瀏覽器便失效。

3. 密碼式(password)

即:對於信任的應用,可以攜帶約定的用戶名和密碼進行令牌申請

流程分析

  1. A 網站使用 B 網站提供的用戶名和密碼,向 B 網站發起令牌請求。
https://oauth.b.com/token?  grant_type=password&  username=USERNAME&  password=PASSWORD&  client_id=CLIENT_ID

grant_type 參數是授權方式,這裡的 password 表示"密碼式"; usernamepassword 是 B 的用戶名和密碼。

  1. B 網站驗證身份後直接將令牌存在 JSON 數據中,作為 HTTP 相應返回令牌給 A 網站。

適用場景:

風險較大,一般適用在對應用高度信任的情況。

4. 客戶端憑證(client credentials)

即:給出憑證讓對方確認並提供令牌

流程分析

  1. A 應用在命令行向 B 發出請求。
https://oauth.b.com/token?  grant_type=client_credentials&  client_id=CLIENT_ID&  client_secret=CLIENT_SECRET

grant_type 參數等於 client_credentials 表示採用憑證式; client_idclient_secret 用來讓 B 確認 A 的身份。

  1. B 網站驗證身份後返回令牌。

這種方式給出的令牌,是針對第三方應用的,而不是針對用戶的,即有可能多個用戶共享同一個令牌。

適用場景:

通過命令行請求令牌。

流程分析

五、使用令牌

當網站獲取到令牌以後,接下來每個 API 請求都需要帶上令牌,其做法是在請求的頭資訊中,將令牌添加 Authorization 欄位中。

六、更新令牌

當令牌有效期到了,OAuth2 允許用戶自動更新令牌,而不用讓用戶重新授權獲取新令牌。

具體流程:

在 B 網站發放令牌時,一次性發放 2 個令牌,一個用於獲取數據,一個用於獲取新的令牌( refresh token 欄位)。令牌到期後,用戶使用 refresh token 發送請求去更新令牌:

https://b.com/oauth/token?  grant_type=refresh_token&  client_id=CLIENT_ID&  client_secret=CLIENT_SECRET&  refresh_token=REFRESH_TOKEN

grant_type 參數為 refresh_token 表示要求更新令牌; client_id 參數和 client_secret 參數用於確認身份; refresh_token 參數就是用於更新令牌的令牌。

B 網站驗證通過以後,就會頒發新的令牌。

參考文章

  1. 部門內部培訓資料
  2. 《OAuth 2 深入介紹》https://dwz.cn/kQo0iNeY
  3. 《阮一峰 理解OAuth 2.0》https://dwz.cn/9XhoQ8DB
  4. 《阮一峰 OAuth 2.0 的四種方式》https://dwz.cn/sbSt4DlZ