OAuth2基本概念和運作流程
OAuth(開放授權)是一個關於授權的開放標準,允許使用者讓第三方應用訪問該使用者在某一網站上儲存的私密的資源(如照片,視訊,聯絡人列表),而無需將使用者名稱和密碼提供給第三方應用。目前的版本是2.0版,本文將對OAuth2.0的一些基本概念和執行流程做一個簡要介紹。主要參考RFC-6749。
應用場景
這裡有兩個典型的例子:
- 比如你瀏覽某個網站的技術文章,發現其中某段介紹的不夠詳細,想留言給作者提問,點選
評論
,結果發現需要有這個網站的賬號才能留言,此時有兩個選擇,一個是新註冊一個此網站的賬號,二是點選通過github快速登入。前者你覺得過於繁瑣,直接點選了github登入,此時,OAuth的認證流程就開始了。通過引導跳轉到github介面,會提示你是否授權該網站使用你的github使用者資訊,點選確認,跳轉回原網站,發現已經使用你的github賬號預設註冊了一個使用者,而且還不需要使用者名稱和密碼,便捷高效。 -
假如有一個雲沖印的網站,可以將你儲存在Google的照片沖印出來,使用者為了使用該服務,必須讓雲沖印讀取Google上的照片。為了拿到照片,雲沖印必須得拿到一個使用者的授權,如何獲取這個使用者授權呢?傳統方法是使用者將使用者名稱和密碼告訴雲沖印,那麼雲沖印就可以自由無限制的訪問了(相當於使用者自己訪問),這樣顯然是不行的,有幾個嚴重的缺點:
- 雲沖印為了儲存後續服務,會儲存使用者的密碼,這樣很不安全。
- 雲沖印擁有了獲取使用者儲存在Google的所有資料的權力,使用者沒法限制雲沖印得到的授權範圍和授權有效期。
- 使用者只有修改密碼,才能收回賦予雲沖印的權力,但是如果還授權給了其他的應用,那麼密碼的修改將影響到所有被授權應用。
- 只要有一個第三方應用程式被破解,就會導致使用者密碼洩漏,以及所有被密碼保護的資料洩漏。(例子來自阮一峰-理解OAuth2.0)
可以看出,OAuth就是為解決如上例子而誕生的。
名詞解釋
以下幾個名詞至關重要:
Resource Owner
:資源所有者。即使用者。Client
:客戶端(第三方應用)。如雲沖印。HTTP service
:HTTP服務提供商,簡稱服務提供商。如上文提到的github或者Google。User Agent
:使用者代理。本文中就是指瀏覽器。Authorization server
:授權(認證)伺服器。即服務提供商專門用來處理認證的伺服器。Resource server
Access Token
:訪問令牌。使用合法的訪問令牌獲取受保護的資源。
執行流程
- (A)客戶端向資源所有者請求授權。授權請求可以直接對資源所有者(如圖所示)進行,或者通過授權伺服器作為中介進行間接訪問(首選方案)。
- (B)資源所有者允許授權,並返回憑證(如
code
)。 - (C)客戶端通過授權伺服器進行身份驗證,並提供授權憑證(如
code
),請求訪問令牌(access token
)。 - (D)授權伺服器對客戶端進行身份驗證,並驗證授權憑證,如果有效,則發出訪問令牌。
- (E)客戶端向資源伺服器請求受保護的資源,並通過提供訪問令牌來進行身份驗證。
- (F)資源伺服器驗證訪問令牌,如果正確則返回受保護資源。
授權
從執行流程不難看出,要獲取access token
必須先得到使用者授權(authorzation grant
),那麼如果獲取這麼使用者授權呢?OAuth 2.0定義了四種類型的授權型別:
- 授權碼模式(
authorization code
) - 簡化模式(
implicit
) - 密碼模式(
resource owner password credentials
) - 客戶端模式(
client credentials
)
授權碼模式(authorization code
)
授權碼模式是功能最完整、使用最廣泛、流程最嚴密的授權模式。
由於這是一個基於重定向的流,所以客戶端必須能夠與資源所有者的使用者代理(通常是web瀏覽器)進行互動,並且能夠從授權伺服器接收傳入的請求(通過重定向)。
- (A)使用者訪問客戶端,客戶端將使用者導向授權伺服器,通過使用者代理(
User-Agent
)傳送包括它的客戶端識別符號、請求的範圍、本地狀態和一個重定向URI,授權伺服器在授予(或拒絕)訪問權後將其傳送給使用者代理。 - (B)授權伺服器對資源所有者進行身份驗證(通過使用者代理),並確定資源所有者是否授予或拒絕客戶端的訪問請求。
- (C)假如資源所有者同意授權請求,那麼授權伺服器將會使用前面提供的或者事先指定的重定向URI(
redirection URI
),重定向到客戶端,並附上一個授權碼(code
)和一個前面提供的本地狀態(state
)(如果有的話,則會原值返回)。 - (D)客戶端收到授權碼,附上早先的
重定向URI
,向授權伺服器申請令牌。這一步是在客戶端的後臺的伺服器上完成的,對使用者不可見。在發出請求時,授權伺服器對客戶端進行身份驗證。請求引數包含授權程式碼、用於獲得驗證的授權程式碼的重定向URI、標識客戶端身份的client id
和client secret
。 - (E)授權伺服器對客戶端進行身份驗證,驗證授權程式碼,並確保所收到的重定向URI與用於在步驟(C)中對客戶端重定向的URI相匹配,如果有效,授權伺服器將傳送訪問令牌
access token
和重新整理令牌refresh token
(可選)。
接著來介紹下各個步驟所需的引數
對於步驟A,客戶端申請授權請求的URI,包含以下引數:
response_type
授權型別。必選項,其值固定為code
。client_id
客戶端id。必選項,用於標識授權伺服器中已註冊的客戶端。redirect_uri
重定向URI。可選項,如果不填寫則使用註冊在授權伺服器端與client_id對應的redirect_uri。scope
申請的許可權範圍,如read
或write
。可選項,如果申請的請求訪問超出授權伺服器定義的可操作範圍則會失敗。state
表示客戶端當前狀態。可選項,可以指定任意值,授權伺服器會原封不動地返回這個值。
eg:
C步驟中,伺服器迴應客戶端的URI,包含以下引數:
code
授權碼。必選項,授權碼必須在頒發後很快過期以減小洩露風險,建議最長時間設為10分鐘,客戶端只能使用該碼一次,否則會被授權伺服器拒絕。該碼與client id
和重定向URI,是一一對應關係。state
如果客戶端的請求中包含這個引數,認證伺服器的迴應也必須一模一樣包含這個引數。
eg:
D步驟中,客戶端向認證伺服器申請令牌的HTTP請求,包含以下引數:
grant_type
許可型別(授權模式)。必選項,此處固定值為authorization_code
。code
上一步獲得的授權碼。必選項。redirect_uri
表示重定向URI。必選項,且必須與A步驟中的該引數值保持一致。client_id
表示客戶端ID,必選項。
eg:
E步驟中,認證伺服器傳送的HTTP回覆,包含以下引數:
access_token
表示訪問令牌。必選項。token_type
表示令牌型別。該值大小寫不敏感,必選項,可以是bearer型別或mac型別。expires_in
表示過期時間,單位為秒。如果省略該引數,必須其他方式設定過期時間。refresh_token
表示更新令牌。可選項,用來獲取下一次的訪問令牌。scope
表示許可權範圍。可選項,如果與客戶端申請的範圍一致,此項可省略。
eg:
簡化模式(implicit
)
簡化模式(implicit grant type)不通過第三方應用程式的伺服器,直接在瀏覽器中向認證伺服器申請令牌,跳過了"授權碼"這個步驟,因此得名。所有步驟在瀏覽器中完成,令牌對訪問者是可見的,且客戶端不需要認證。具體步驟可參閱RFC6749 4.2節。
密碼模式(resource owner password credentials
)
密碼模式中,使用者向客戶端提供自己的使用者名稱和密碼。客戶端使用這些資訊,向"服務商提供商"索要授權。 在這種模式中,使用者必須把自己的密碼給客戶端,但是客戶端不得儲存密碼。這通常用在使用者對客戶端高度信任的情況下,比如客戶端是作業系統的一部分,或者由一個著名公司出品。而認證伺服器只有在其他授權模式無法執行的情況下,才能考慮使用這種模式。可參閱RFC6749 4.3節。
客戶端模式(client credentials
)
客戶端模式(Client Credentials Grant)指客戶端以自己的名義,而不是以使用者的名義,向"服務提供商"進行認證。嚴格地說,客戶端模式並不屬於OAuth框架所要解決的問題。在這種模式中,使用者直接向客戶端註冊,客戶端以自己的名義要求"服務提供商"提供服務,其實不存在授權問題。可參閱RFC6749 4.4節。
基於Github登入的授權碼模式例子
前文提到了一個Github登入留言的例子,假設我們要使用OAuth2.0協議搭建一個網站,利用Github作為授權和資源伺服器,實現第三方登入功能。 (轉載)現概況一下主要流程:
1) 網站和Github之間的協商
Github會對使用者的許可權做分類比如讀取倉庫資訊的許可權、寫入倉庫的許可權、讀取使用者資訊的許可權、修改使用者資訊的許可權等等。如果我想獲取使用者的資訊,Github會要求我,先在它的平臺上註冊一個應用,在申請的時候標明需要獲取使用者資訊的哪些許可權,並且在申請的時候填寫你的網站域名,Github只允許在這個域名中獲取使用者資訊。
此時我的網站已經和Github之間達成了共識,Github也給我發了兩張門票,一張門票叫做Client Id
,另一張門票叫做Client Secret
。
2)使用者和Github之間的協商
使用者進入我的網站,點選github登入按鈕的時候,我的網站會將Github給我的Client Id
交給使用者,讓他進入Github授權介面,如果此時使用者沒有登入,Github會提示登入(當然這不是OAuth2.0客戶端部分應該關注的)。假設使用者已經登入Github,那麼Github看到使用者手中的門票,就知道是我的網站讓他過來的,於是就把我的網站獲取的許可權擺出來,並詢問使用者是否允許網站獲取這些許可權。
如果使用者同意,在授權頁面點選了確認授權後,頁面會跳轉到我預先設定的 redirect_uri
並附帶一個蓋了章的門票code
。
這個時候,使用者和 Github 之間的協商就已經完成,Github 也會在自己的系統中記錄這次協商,表示該使用者已經允許在我的網站訪問上直接操作和使用他的部分資源。
3)告訴Github我的網站要來訪問
第二步中,我們已經拿到了蓋過章的門票code
,但這個code
只能表明,使用者允許我的網站從github上獲取該使用者的資料,如果我直接拿這個code
去github訪問資料一定會被拒絕,因為任何人都可以持有code
,github並不知道code
持有方就是我本人。
還記得之前申請應用的時候github給我的兩張門票麼,Client Id
在上一步中已經用過了,接下來輪到另一張門票Client Secret
。
拿著使用者蓋過章的code
和能夠標識個人身份的client_id
、client_secret
去拜訪 github,拿到最後的綠卡access_token
。
4)使用者開始使用Github賬號在我的網站上留言
上一步github已經把最後的綠卡access_token
給我了,通過github提供的 API 加綠卡就能夠訪問使用者的資訊了,能獲取使用者的哪些許可權在response
中也給了明確的說明,scope
為user
和gist
,也就是隻能獲取user
組和gist
組兩個小組的許可權,user
組中就包含了使用者的名字和郵箱等資訊了。