OAuth2.0授權登入
最近工作中遇到了多系統間的授權登入,對OAuth2.0進行了學習研究,並總結備忘。
【場景】
我們登入一些論壇等網站的時候,如果不想單獨註冊該網站賬號,可以選擇用微信或QQ賬號進行授權登入。 這樣的第三方登入方式到底是怎麼實現的呢?難道是騰訊把我們微信或QQ賬號資訊賣給了這些網站?很顯然,騰訊是不會這麼幹的,這種登入方式的實現,其實就是使用的OAuth2.0的授權登入方式。
類似地,在公司內部,如果公司有多套不同的軟體系統,例如公司內網的財務報銷系統、考勤系統、報銷系統、人事系統等,也可以實現一個員工賬號就能授權訪問所有系統,而不需要每個系統都開通單獨的賬號,設定獨立的密碼。這是通常所說的SSO單點登入,而OAuth2.0是單點登入的實現方式之一。
【定義】
OAuth2.0是一種允許第三方應用程式使用資源所有者的憑據獲得對資源有限訪問許可權的一種授權協議。
例如在上述例子中,通過微信登入論壇的過程,就相當於微信允許該論壇作為第三方應用程式在經過微信使用者授權後,通過微信頒發的授權憑證有限地訪問使用者的微信頭像、手機號,性別等受限制的資源,從而來構建自身的登入邏輯。
注:在OAuth2.0協議中,第三方應用程式獲取的憑證並不等同於資源擁有者持有的使用者名稱和密碼,以上面例子來說,微信是不會直接將使用者的使用者名稱、密碼等資訊作為憑證返回給該論壇的。這種授權訪問憑證一般來說就是一個表示特定範圍、生存週期和其訪問許可權的一個由字串組成的訪問令牌,也就是我們常說的token。在這種模式下OAuth2.0協議中通過引入一個授權層來將第三方應用程式與資源擁有者進行分離,而這個授權層也就是常說的“auth認證服務/sso單點登入伺服器”。
在OAuth2.0協議中定義了以下四個角色:
1)resource owner(資源擁有者)
即能夠有權授予對保護資源訪問許可權的實體。例如我們使用通過微信賬號登入論壇,而微信賬號資訊的實際擁有者就是微信使用者,也被稱為終端使用者。
2)resource server(資源伺服器)
承載受保護資源的伺服器,能夠接收使用訪問令牌對受保護資源的請求並響應,它與授權伺服器可以是同一伺服器(下述的“簡化模式”),也可以是不同伺服器(下述的“授權碼模式”)。在上述例子中該角色就是微信伺服器。
3)client(客戶端)
代表資源所有者及其授權發出對受保護資源請求的應用程式。在上面的例子中的使用者要註冊的論壇就是該角色。
4)authorization server(授權伺服器)
認證伺服器,即服務提供商專門用來處理認證授權的伺服器。例如微信開放平臺提供的認證服務的伺服器。
【流程】
還是以微信授權登入舉例,流程如下:
流程說明:
- 首先微信使用者點選豆瓣網論壇的微信授權登入按鈕後,豆瓣網論壇會將請求通過URL重定向的方式跳轉至微信使用者授權介面;
- 此時微信使用者實際上是在微信上進行身份認證,與豆瓣網論壇並無互動了,這一點非常類似於購物後,使用網銀支付的場景;
- 使用者使用微信客戶端掃描二維碼認證或者輸入使用者名稱密碼後,微信會驗證使用者身份資訊的正確性,如正確,則認為使用者確認授權微信登入豆瓣網論壇,此時會先生成一個臨時憑證,並攜帶此憑證通過使用者瀏覽器將請求重定向回豆瓣網在第一次重定向時攜帶的callBackUrl地址;
- 之後使用者瀏覽器會攜帶臨時憑證訪問豆瓣網服務,豆瓣網則通過此臨時憑證再次呼叫微信授權介面,獲取正式的訪問憑據access_token;
- 在豆瓣網獲取到微信授權訪問憑據access_token後,此時使用者的授權基本上就完成了,後續豆瓣網要做的只是通過此token再訪問微信提供的相關介面,獲取微信允許授權開發的使用者資訊,如頭像,暱稱等,並據此完成自身的使用者邏輯及使用者登入會話邏輯。
【模式】
在上述流程中比較關鍵的動作是resource owner(資源擁有者,使用者)怎麼樣才能給Client(豆瓣網論壇)授權,因為只有有了這個授權,Client角色才可以獲取訪問令牌(access_token),進而通過令牌訪問其他資源介面。而關於客戶端如何獲得授權的問題,在OAuth2.0中定義了四種授權方式,目前微信授權登入,使用的就是其中最常用的一種:“授權碼模式”(authorization_code)。
OAuth2.0定義了四種授權模式,它們分別是:
- 授權碼模式(authorization code)
- 簡化模式(implicit)
- 密碼模式(resource owner password credentials)
- 客戶端模式(client credentials)
四種模式對比說明:
1.授權碼模式(authorization code)
功能最完整、流程最嚴密的授權模式,安全性最高,也最為常用。
特點是通過客戶端的後臺伺服器,與“服務提供商”的認證服務進行互動(如微信開放平臺),如上述微信賬號登入豆瓣網的流程就是授權碼模式的實現。
這種模式下授權程式碼並不是客戶端直接從資源所有者獲取,而是通過授權伺服器(authorization server)作為中介來獲取,授權認證的過程也是資源所有者直接通過授權伺服器進行身份認證,避免了資源所有者身份憑證與客戶端共享的可能,因此是十分安全的。
2.簡化模式(implicit grant type)
簡化模式是對授權碼模式的簡化,用於在瀏覽器中使用指令碼語言如JS實現的客戶端中,特點是不通過客戶端應用程式的伺服器,而是直接在瀏覽器中向認證伺服器申請令牌,跳過了“授權碼臨時憑證”這個步驟。其所有的步驟都在瀏覽器中完成,令牌對訪問者是可見的,且客戶端不需要認證。如果使用此種授權方式來實現微信登入豆瓣網論壇的過程流程示例如下:
可以看出,與授權碼模式的不同之處在於:在第4步使用者完成授權後,認證伺服器是直接返回了access_token令牌至使用者瀏覽器端,而並沒有先返回臨時授權碼code,然後由客戶端的後端服務去通過授權碼再去獲取access_token令牌,從而省去了一個跳轉步驟,提高了互動效率。
但是由於這種方式訪問令牌access_token會在URL片段中進行傳輸,因此可能會導致訪問令牌被其他未經授權的第三方擷取,所以安全性上並不是那麼的強壯。
3.密碼模式(resource owner password credentials)
在密碼模式中,使用者需要向客戶端提供自己的使用者名稱和密碼,客戶端使用這些資訊向“服務提供商”索要授權。這相當於在豆瓣網中使用微信登入,我們需要在豆瓣網輸入微信的使用者名稱和密碼,然後由豆瓣網使用我們的微信使用者名稱和密碼去向微信伺服器獲取授權資訊。
這種模式一般用在使用者對客戶端高度信任的情況下,因為雖然協議規定客戶端不得儲存使用者密碼,但是實際上這一點並不是特別好強制約束。就像在豆瓣網輸入了微信的使用者名稱和密碼,豆瓣網儲存不儲存我們並不是很清楚,所以安全性是非常低的。因此一般情況下是不會考慮使用這種模式進行授權的。公司內網之間相互信任的子系統間也許才有可能使用。
如果使用此種授權方式來實現微信登入豆瓣網論壇的過程流程示例如下:
4.客戶端模式(client credentials)
客戶端模式是指客戶端以自己的名義,而不是以使用者的名義,向“服務提供方”進行認證。嚴格地說,客戶端模式並不屬於OAuth2.0協議所要解決的問題。在這種模式下,使用者並不需要對客戶端授權,使用者直接向客戶端註冊,客戶端以自己的名義要求“服務提供商”提供服務,其實不存在授權問題。
步驟如下:
(1)客戶端向認證伺服器進行身份認證,並要求一個access_token令牌。
(2)認證伺服器確認無誤後,向客戶端提供訪問access_token令牌。
(3)客戶端後續攜帶access_token令牌繼續後續業務請求流程。
步驟(1)中,客戶端發出的HTTP請求,包含以下引數:
- grant_type:表示授權型別,此處的值固定為"client_credentials",必選項。
- scope:表示許可權範圍,可選項。
示例: POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=client_credentials
認證伺服器必須以某種方式,驗證客戶端身份。然後在步驟(2)中,認證伺服器向客戶端提供訪問access_token令牌,下面是一個例子。
示例: HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZFEjr1zCsicMWpAA", "iss":"簽發授權者",
"sub":"服務使用者",
"aud":"服務提供者",
"exp":"過期時間", "iat":"簽發時間", "other_custom_parameter":"value" }
綜上所述,雖然在OAuth2.0協議中定義了四種客戶端授權認證模式,但是實際上大部分實際應用場景中使用的都是授權碼(authorization code)的模式,如微信開放平臺、微博開放平臺等使用的基本都是授權碼認證模式。
【更新令牌】
如果使用者訪問的時候,客戶端的"訪問令牌"已經過期,則需要使用"更新令牌"申請一個新的訪問令牌。
客戶端發出更新令牌的HTTP請求,包含以下引數:
- grant_type:表示使用的授權模式,此處的值固定為"refresh_token",必選項。
- refresh_token:表示早前收到的更新令牌,必選項。
- scope:表示申請的授權範圍,不可以超出上一次申請的範圍,如果省略該引數,則表示與上一次一致。
示例: POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
&n