第 87 天:Python Web開發 OAuth2.0 簡介

  • 2019 年 12 月 21 日
  • 筆記

我們經常看到或者用到一些可以使用微博、微信、支付寶等帳號登錄的應用,還有一些微博助手、微信公眾號助手、客戶端之類的東西,這些是怎麼做的呢,背後的原理是什麼呢?為什麼公眾號配置起來那麼繁瑣呢?什麼是 access token、refresh token?等等,今天我們來了解下開放式授權模式 OAuth

隨著互聯網的發展,各種應用相互交叉,到處需要用戶登錄,資訊安全成為了不可迴避的問題,應用需要擴展,用戶需要更好的體驗,資訊需要更安全的保障,為了滿足這些需求,互聯網技術不斷推陳出新,從通訊安全,到各種協議框架,有無數的解決方案。

其中 OAuth 框架是很閃耀的一個,一經推出,就得到各大互聯網公司的積極響應,到2010年推出了 OAuth 2.0, 不但修補了 1.0 的安全漏洞,而且簡化了授權流程,得到更廣泛的應用,成為了主流。從 PC 到 Web,從移動端到物聯網,越來越多的應用構建在 OAuth 框架之上,那麼 OAuth 是什麼呢?

OAuth 是什麼

OAuth 框架提供了一種認證和授權機制,可以讓用戶將其受保護的資源授權給其他應用來訪問或者使用。

阮一峰老師對 授權有個形象比喻:授權機制相當於你給快遞員一個臨時密碼(授權),快遞員可以使用這個密碼打開小區門禁,將快遞送到你家門口,而後臨時密碼將失效(詳見參考鏈接)。

這個例子中,你就是 用戶,小區是 受保護的資源,快遞員是其他應用(第三方應用)。

如果沒有這個機制:

  • 要麼你就得去小區門口拿快遞,不方便,
  • 要麼就得告訴所有可能給你送快遞的快遞員門禁密碼,不安全

有了 OAuth 框架(協議),既方便,又安全

OAuth2.0

這裡不打算介紹 OAuth 1.0,原因是:

  • OAuth 1.0 存在安全漏洞
  • OAuth 1 和 後面的 OAuth 1a 交流流程比較複雜
  • OAuth 2.0 安全性好,應用更廣泛

如果想了解更多關於 OAuth 的知識,請訪問參考鏈接

角色

OAuth2.0 實際上就是讓第三方服務獲得用戶在資源伺服器上的授權的過程,會涉及到 4 種角色

  • 資源擁有者(Resource owner),即用戶
  • 認證伺服器(Authorization server),用來認證用戶憑證,頒布授權碼的伺服器
  • 資源伺服器(Resource Server),存放用戶受保護的資源的伺服器
  • 第三方應用(Client),也稱之為客戶端(後續皆稱 客戶端),需要得到用戶授權,以便訪問用戶受保護的資源的應用程式

不是任何客戶端都能得到授權的,在開通 OAuth 授權之前,需要先到認證伺服器或者資源伺服器上註冊,註冊成功會得到 appidapp_secret,用來向認證伺服器表明應用的身份

角色之間關聯

了解了 OAuth2.0 框架中的主要角色,有必要了解下角色之間關聯關係

  • 認證伺服器 和 資源伺服器:通常來說,認證伺服器和資源伺服器同屬於一個服務商,它們就有天生的關聯,而且是內部的安全的,甚至它們可以部署在同一個伺服器(Web 伺服器)上
  • 用戶 和 服務商:對於像 Github、微信這樣的知名應用,用戶會主動在這些應用或服務上註冊,填寫的資料資訊,設置的昵稱,產生的文章、上傳的照片,等等將成為用戶的資源,這些資源被存放在資源伺服器上
  • 客戶端 和 服務商:客戶端,即第三方應用,要從服務商的資源伺服器中獲取數據,給用戶提供額外服務,必須在服務商處註冊,提供應用的基本資訊,認證資訊,服務域名,申請用戶授權的範圍、甚至企業資質(例如申請微信公眾號的企業服務)等等,申請通過後,服務商會返回 appidapp_secret,作為客戶端和服務商的交互憑證
  • 用戶 和 客戶端:客戶端提供了特別的服務,可以吸引到用戶,為了讓用戶體驗更好,引導用戶通過授權的方式,從而獲取授權範圍內的用戶資訊,例如 Openid,作為用戶在客戶端上的唯一標識,從而建立起和用戶的關聯

至此,四個角色之間的關聯就建立好了,下面開始介紹具體的授權方式

基本授權流程

沒有比圖更能說明白流程的,借用 RFC6749 文檔中的圖:

授權流程圖

  • A 客戶端,向用戶發出授權請求
  • B 用戶同意或者拒絕客戶端的授權請求,假設是同意
  • C 客戶端拿著用戶的授權請求認證伺服器做認證
  • D 如果 C 通過認證,認證伺服器將返回 Access Token,即可以訪問資源的令牌
  • E 客戶端使用 Access Token 請求資源伺服器上的資源
  • F 資源伺服器驗證了 Access Token 後,返回受保護的資源

流程中最核心的是讓客戶端獲得 Access Token,之後在訪問受保護資源時,就不需要用戶反覆授權了

Access Token 顯然不是用戶在資源伺服器上的密碼,是有認證伺服器頒發的,那麼也可以被銷毀

Access Token 和之前課程中的 JWT 是類似的,實際上 JWT 是 OAuth 認證的一個特例

授權模式

根據授權流程,OAuth2.0 定義了 4 種針對不同應用場景的授權模式

  • 授權碼模式(authorization code)
  • 簡化模式(implicit)
  • 密碼模式(resource owner password credentials)
  • 客戶端模式(client credentials)

授權碼模式

授權碼模式是最完整,安全性最高的授權模式,也是最常用的一種模式,其特點是通過客戶端的後台伺服器與認證伺服器交互,如圖:

授權碼模式流程圖

注意:上圖中的步驟 A, B, C 在通過用戶代理端( User-Agent 一般指瀏覽器)時,被拆分成了兩部分

  • A 用戶訪問客戶端的客戶端,後者將前者導向認證伺服器
  • B 用戶確定是否授權給客戶端
  • C 假設用戶給予授權,認證伺服器將用戶導向客戶端事先指定的"重定向URI"(Redirection URI),同時附上一個授權碼
  • D 客戶端收到授權碼,通過後台伺服器,附上早先的"重定向URI",向認證伺服器申請令牌。這一步對用戶不可見
  • E 認證伺服器核對了授權碼和重定向URI,確認無誤後,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)

接下來說明一下過程中所包含一下參數

不同認證伺服器上的參數名稱有可能不同,但含義相同,例如 client_id 一般被 appid 代替

步驟 A,客戶端申請認證的 URI,包含以下參數:

  • response_type:表示授權類型,必選項,此處的值固定為 code,因為需要先獲取授權碼
  • client_id:表示客戶端的ID,必選項,是在認證伺服器分配給客戶端的id,即 appid
  • redirect_uri:表示重定向URI,可選項
  • scope:表示申請的許可權範圍,可選項
  • state:表示客戶端的當前狀態,可以指定任意值,認證伺服器會原封不動地返回這個值

步驟 C,認證伺服器回應的 URI,包含以下參數:

  • code:表示授權碼,必選項。該碼的有效期應該很短,通常設為 10 分鐘,客戶端只能使用該碼一次,否則會被認證伺服器拒絕。該碼與客戶端 ID 和重定向 URI,是一一對應關係
  • state:如果客戶端的請求中包含這個參數,認證伺服器的回應也必須一模一樣包含這個參數

步驟 D,客戶端向認證伺服器申請令牌的 HTTP 請求,包含以下參數:

  • grant_type:表示使用的授權模式,必選項,此處的值固定為 authorization_code
  • code:表示上一步獲得的授權碼,必選項
  • redirect_uri:表示重定向 URI,必選項,且必須與 A 步驟中的該參數值保持一致
  • client_id:表示客戶端 ID,必選項

步驟 E,認證伺服器發送的 HTTP 響應,包含以下參數:

  • access_token:表示訪問令牌,必選項
  • token_type:表示令牌類型,該值大小寫不敏感,必選項,可以是bearer類型或mac類型
  • expires_in:表示過期時間,單位為秒。如果省略該參數,必須其他方式設置過期時間
  • refresh_token:表示更新令牌,用來獲取下一次的訪問令牌,可選項
  • scope:表示許可權範圍,如果與客戶端申請的範圍一致,此項可省略

認證伺服器會以 JSON 的形式返回 access_token 數據,且不允許做快取,以提高安全性

簡化模式

簡化模式不需要通過客戶端後台伺服器,直接在瀏覽器中向認證伺服器申請令牌,跳過了授權碼這個步驟,因此得名。所有步驟在瀏覽器中完成,令牌對訪問者是可見的,且客戶端不需要認證,如圖:

簡化模式流程圖

  • A 客戶端將用戶導向認證伺服器
  • B 用戶決定是否給予客戶端授權
  • C 假設用戶給予授權,認證伺服器將用戶導向客戶端指定的重定向URI,並在URI的Hash部分包含了訪問令牌 access_token
  • D 瀏覽器向資源伺服器發出請求,其中不包括上一步收到的Hash值
  • E 資源伺服器返回一個網頁,其中包含的程式碼可以獲取Hash值中的令牌
  • F 瀏覽器執行上一步獲得的腳本,提取出令牌
  • G 瀏覽器將令牌發給客戶端

接下來說明一下過程中所包含一下參數

步驟 A,客戶端發送 HTTP 請求,包含的參數:

  • response_type:表示授權類型,此處的值固定為 token,必選項
  • client_id:表示客戶端的ID,必選項
  • redirect_uri:表示重定向的URI,可選項
  • scope:表示許可權範圍,可選項
  • state:表示客戶端的當前狀態,可以指定任意值,認證伺服器會原封不動地返回這個值

步驟 C,認證伺服器回應客戶端的 URI,包含以下參數:

  • access_token:表示訪問令牌,必選項。
  • token_type:表示令牌類型,該值大小寫不敏感,必選項
  • expires_in:表示過期時間,單位為秒。如果省略該參數,必須其他方式設置過期時間
  • scope:表示許可權範圍,如果與客戶端申請的範圍一致,此項可省略
  • state:如果客戶端的請求中包含這個參數,認證伺服器的回應也必須一模一樣包含這個參數

有個需要注意的地方,步驟 C,返回的 access_token 放在重定向 URL 的 Fragment 中,即錨點中, # 後面,例如

http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA  &state=xyz&token_type=example&expires_in=3600

因為錨點中的內容不會發送給後台,從而減少了一次數據傳輸,降低了一定風險

對於簡化模式獲得的 access_token 有效期很短,一般是會話級的,即當會話結束時就失效

密碼模式

密碼模式中,用戶向客戶端提供自己的用戶名和密碼,客戶端使用這些資訊,向"服務商提供商"索要授權。

在這種模式中,用戶必須把自己的密碼給客戶端,但是客戶端不得儲存密碼(既然用戶信任你,你就必須兌現這個承諾)。

密碼模式的特性決定,需要用在用戶對客戶端高度信任的情況下,比如客戶端是作業系統的一部分,或者由一個著名公司出品,而認證伺服器只有在其他授權模式無法執行的情況下,才能考慮使用這種模式

這裡只簡單介紹下,不做做詳細講解,如有興趣了解,可以查閱文末參考

客戶端模式

客戶端模式指客戶端以自己的名義,而不是以用戶的名義,向"服務提供商"進行認證。嚴格地說,客戶端模式並不屬於 OAuth 框架所要解決的問題

在這種模式中,用戶直接向客戶端註冊,客戶端以自己的名義要求"服務提供商"提供服務,其實不存在授權問題

客戶端模式,就像二道販子(只為借用比喻,並無貶義),將原始服務包裝後,再提供給最終用戶,常見於多租戶的 Saas 系統,例如統一提供支付通道、處理 GPS 資訊等

這裡也只簡單介紹下,如有興趣了解,可以查閱文末參考

刷新 access_token

在 授權碼模式中,授權伺服器可以會同時返回 refresh_token,用來在 access_token 過期前,重新獲取新的access_token,不需要用戶重新確認授權,有助於提高用戶體驗

在 access_token 過期前,客戶端可用 refresh_token 向授權伺服器發送請求,例如,假設 b.com 是授權伺服器地址,請求大體是:

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: 客戶端 id,即第三方應用在授權伺服器上註冊被分配的 id
  • client_secret: 客戶端和授權伺服器通行的密鑰,由授權伺服器頒發,在特殊需要確認的情況下需要作為驗證條件
  • refresh_token: 用戶獲取新的 access_tokenrefresh_token

安全的 OAuth2.0

上面較為詳細的講述了 OAuth2.0 框架,了解了在開放網路中如何安全的獲取用戶授權的技術細節,但再完善的交互方案、再複雜的嚴密的通訊過程,都避免不了中間人攻擊,當客戶端和認證伺服器之間通過 http 協議交互數據時,會被截取通訊內容,從而獲得用戶的授權,這是不能接受的,所以 OAuth2.0 需要建立在 https 協議上,將通訊內容加密,最大程度的防止資訊被竊取。

如果 OAuth2.0 框架用在敏感資訊交互上時,必須使用 https 協議確保安全,但並不是說只能支援 https,對於非敏感數據,或者不重要的授權,可以使用 http 協議作為通訊方式

總結

本節課程著重介紹了 OAuth2.0 授權框架,從它的作用,到具體的技術細節,做了較為詳細的講述,如果需要用安全的授權,需要將通訊建立在 https 協議之上。由於 OAuth 概念較多,流程複雜,這節沒有涉及到具體的編程實踐,下一節,我們以 Github 為例,使用之前介紹過的 Authlib 模組,用 Flash 實現一個第三方應用,作為實踐,敬請期待

參考

  • RFC 6749: https://tools.ietf.org/html/rfc6749
  • 比喻: http://www.ruanyifeng.com/blog/2019/04/oauth_design.html
  • http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
  • https://docs.authlib.org/en/stable/basic/oauth2.html