理解OAuth2
[TOC]json
Oatuh2用來作什麼
有這樣一種場景,一個用戶(假設是QQ),但願讓一個第三方的應用(好比說某個論壇),可以獲得關於自身的一些信息(惟一用戶標識,好比說QQ號,用戶我的信息,好比說是一些基礎資料,暱稱和頭像等)。可是在得到這些資料的同時,卻也不能提供用戶名和密碼之類的驗證信息。好比說用戶不可能將自身的用戶名和密碼給第三方讓第三方到用戶中心之類的地方去獲取信息。要達到這樣的結果確定有許多的實現方式。而Oatuh2就是實現上述目標的一種規範,或者說是具體實現的指導方案。瀏覽器
歡迎加入技術交流羣186233599討論交流,也歡迎關注技術公衆號:風火說。安全
Oauth2具體作法
首先來了解下Oatuh2中的幾個名字,方便下文的闡述。服務器
- Third-party application: 第三方應用
- Resource Owner: 資源持有者,通常就是用戶自身
- Authorization server: 認證服務器
- Resource server: 資源服務器,即具體資源的存儲方。與認證服務器是不一樣的邏輯節點,可是在物理上,雙方是能夠在一塊兒的
- User Agent: 用戶代理,通常就是指的瀏覽器
- Http Service: 服務提供者,也就是持有Resource Server的存在方。能夠理解爲相似QQ,或者微信這樣具有用戶信息的服務者。
Oauth2的做用就是讓第三方應用在用戶(資源持有者)受權的狀況下,經過認證服務器的認證,從而安全的在資源服務器上得到對應的用戶資源的流程指導。微信
Oauth2的流程
Oauth2,根據RFC6749文檔,大體的流程以下圖所示

上圖中的client就是第三方應用。能夠看到,Oauth的大體思路是一個線性的流程。網絡
- 第三方應用向資源持有者請求獲取資源
- 資源持有者受權給予第三方應用一個許可
- 第三方應用將該許可給予認證服務器進行認證,若是認證成功,返回一個Access Token
- 第三方應用使用該access token到資源服務器處獲取該access token對應的資源(也就是第一步中資源持有者自身的資源)
在上面的這個流程中,其中第二步,資源持有者如何受權給予第三方應用一個許可就是最爲關鍵的地方。其中RFC6749文檔給出4種第三方取得受權許可的方式。session
- 受權碼模式
- 簡化模式
- 密碼模式
- 客戶端模式
其中受權碼模式是步驟流程最爲詳細嚴謹的一種模式。而網絡上大部分的第三方Oauth2實現都是基於受權碼模式的。本文也是主要講解受權碼模式中的相關流程性問題。至於其餘的三種模式,讀者能夠自行參看RFC6749文檔。app
受權碼模式
受權碼模式的流程以下圖所示

下面來分別講解其中的幾個點加密
第三方引導用戶跳轉至認證服務器的受權頁面
在引導跳轉的時候須要攜帶以下的幾個參數url
- response_type:受權類型。受權碼模式下,就固定爲code
- app_id:第三方應用的標識id。
- redirect_uri:重定向uri,也就是在受權成功後認證服務器讓用戶重定向的地址。通常而言也就是當前用戶在第三方應用中最初的請求地址
- scope:受權範圍。可選內容,能夠根據第三方應用和實現方的要求自行制定合適的值。
- state:透明的驗證參數。RFC6749文檔推薦認證服務器在重定向的時候應該原封不一樣的返還這個參數。注意,該參數嚴格來講應該是一個必須參數。用來防止CSRF攻擊。也就是說用於讓第三方服務器驗證重定向回來的uri的確是認證服務器的行爲而不是其餘的攻擊者僞造的。通常來講跳轉到認證服務器的受權頁面是走的https,可是認證服務器重定向到回調地址的時候可能走的就是http。此時code存在泄漏以及url存在被僞造的風險。那麼第三方應用必需要有辦法驗證該回調是否的確由認證服務器發起,而且的確是以前本身的受權請求致使的回調。作法其實也不復雜,就是在session中保存一個隨機值,做爲state參數。認證服務器回調的時候帶上該state參數,第三方應用驗證該參數是否與本身session中的state參數值一致便可。若是認證的受權頁面不是https加密的,那麼在發出請求的時候,認證state參數可能會被竊取。那麼這個時候還有另一種作法。也就是第三方應用發送的是加密後的state參數,而認證服務器重定向的時候攜帶的是解密後的state參數。第三方應用只要在session中判斷解密後的值是否與session的一致,也能夠達到防止攻擊的目的。這樣,受權頁面也就是能夠走在普通的http之中了。
用戶選擇是否給予受權
這一步是一個用戶行爲。目前基本的作法都是讓用戶在受權頁面上輸入用戶名和密碼。爲了保證安全性,這個頁面須要由https來進行保護。固然,若是有其餘的方式來保證用戶名密碼,以及認證的state參數不會泄露也是能夠的。若是用戶輸入正確的用戶名和密碼,通常就確認爲用戶給予受權。
認證服務器生成code而且讓用戶重定向至指定的url
若是用戶給予受權,則認證服務器須要生成一個惟一的受權碼code。該code的時效性應該比較短,在5分鐘之內比較合適。而且該code只能使用一次,下次就會失效。同時,該code與客戶端的id,redirect-uri參數是一一對應的關係。認證服務器此時應該讓用戶重定向至一開始指定的redirect_uri。攜帶上state和code參數
第三方應用使用code到認證服務器處兌換令牌access token
第三方應用在驗證過state參數的正確性後,接着就可使用code到認證服務器處換取token。這一步,第三方應用須要攜帶上的參數有
- code:就是認證服務器給予的code參數
- appid:客戶端的惟一標識
- redirect_uri:也就是第一步請求中的重定向參數。由於code其實是與appid和redirect_uri一一對應的。因此用code換取令牌的時候也要攜帶上這兩個參數
- grant_type: 受權模式,這裏固定爲"authorization_code"
- appkey:用於驗證應用的身份。appid和appkey能夠理解爲應用本身的用戶名和密碼。
oauth2的服務器自己都是走https。因此均可以直接明文傳輸不須要考慮安全性問題。不過若是不是http的,也能夠直接參數用戶名密碼登陸的方式,就是給appkey進行md5運算。
關於爲什麼不直接傳遞accesstoken的問題,是基於安全考慮。由於認證服務器是基於Https,而第三方應用能夠是http的。若是在回調的時候直接帶上accesstoken,就存在着泄露的問題。
認證服務器返回accesstoken
認證服務器在驗證過參數的合法性後,生成一個全局惟一的token,而且返回給第三方應用。返回的內容採用json表示,返回的參數主要有
- access_token: 用於獲取對應資源的令牌
- expires_time: 該令牌的有效期
- reflesh_token: 用戶獲取新的accesstoekn的token。因爲accesstoken的有效期比較短,一旦失效,用戶須要再走上面的流程是比較繁瑣的。爲了提高用戶體驗,可使用reflesh_token來獲取新的accesstoken。不過這個作法,已經有不一樣的實現方將這個返回參數去掉了。由於實際上reflesh_token也就意味着accesstoekn是永久有效的了。那和直接延長accesstoken的有效期也沒有直接區別了。