by 太陽雪
我們經常看到或者用到一些可以使用微博、微信、支付寶等賬號登錄的應用,還有一些微博助手、微信公衆號助手、客戶端之類的東西,這些是怎麼做的呢,背後的原理是什麼呢?爲什麼公衆號配置起來那麼繁瑣呢?什麼是 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 授權之前,需要先到認證服務器或者資源服務器上註冊,註冊成功會得到
appid
和app_secret
,用來向認證服務器表明應用的身份
角色之間關聯
瞭解了 OAuth2.0 框架中的主要角色,有必要了解下角色之間關聯關係
- 認證服務器 和 資源服務器:
通常來說,認證服務器和資源服務器同屬於一個服務商,它們就有天生的關聯,而且是內部的安全的,甚至它們可以部署在同一個服務器(Web 服務器)上 - 用戶 和 服務商:
對於像 Github、微信這樣的知名應用,用戶會主動在這些應用或服務上註冊,填寫的資料信息,設置的暱稱,產生的文章、上傳的照片,等等將成爲用戶的資源,這些資源被存放在資源服務器上 - 客戶端 和 服務商:
客戶端,即第三方應用,要從服務商的資源服務器中獲取數據,給用戶提供額外服務,必須在服務商處註冊,提供應用的基本信息,認證信息,服務域名,申請用戶授權的範圍、甚至企業資質(例如申請微信公衆號的企業服務)等等,申請通過後,服務商會返回appid
和app_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,即 appidredirect_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,即第三方應用在授權服務器上註冊被分配的 idclient_secret
: 客戶端和授權服務器通行的密鑰,有授權服務器頒發,在特殊需要確認的情況下需要作爲驗證條件refresh_token
: 用戶獲取新的access_token
的refresh_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
關注公衆號:python技術,回覆"python"一起學習交流