OAuth2.0 知多少

1. 引言

週末逛簡書,看了一篇寫的極好的文章,點擊大紅心點贊,就直接給我跳轉到登陸界面了,原來點贊是須要登陸的。數據庫

簡書登陸界面

但是沒有我並無簡書帳號,一直使用的QQ的集成登陸。下面有一排社交登陸按鈕,咱們能夠用第三方社交帳號登錄便可。點擊QQ圖標,就給我跳轉到了QQ登陸受權頁面,以下圖:瀏覽器

QQ登陸受權頁面

從圖片上咱們能夠看到主要包括兩個部分,一個是左邊的用戶登陸,一個是右邊告知簡書將獲取哪些權限。輸入QQ帳號和密碼,點擊受權並登陸,就成功登陸到簡書了,併成功獲取到了我QQ的帳號和暱稱,以下圖:安全

簡書成功獲取到QQ暱稱和頭像

簡書集成的社交登陸,大大簡化了咱們的註冊登陸流程,真是一號在手上網無憂啊。
這看似簡單的集成,但背後的技術原理『OAuth2.0』可沒那麼簡單,那咱們廢話很少說,一探究竟吧。服務器

2. OAuth 2.0

OAuth 2.0是用於受權的行業標準協議。OAuth 2.0取代了在2006年建立的原始OAuth協議上所作的工做。OAuth 2.0專一於客戶端開發人員的簡單性,同時爲Web應用程序,桌面應用程序,手機和客廳設備提供特定的受權流程。cookie

在傳統的client-server認證模型中,客戶端請求訪問服務器上受限的資源(protected resource),須要經過使用資源全部者(resource owner)的憑證在服務器上進行認證。爲了支持第三方應用程序訪問受限資源,資源全部者須要向第三方應用共享其憑證。這就會形成如下問題:網絡

  1. 第三方應用爲了後續使用,會存儲資源全部者的憑證主要是密碼。
  2. 服務端須要支持密碼認證,儘管密碼認證不安全。
  3. 第三方應用得到對資源的過分訪問而不只侷限於受限資源,且資源全部者沒有辦法對其進行限制。
  4. 資源全部者沒法收回權限,除非修改密碼。
  5. 若是第三方應用的密碼被破解,就會致使全部被該密碼保護的數據被泄露。

想想這樣一個場景,若是簡書是直接使用QQ用戶名密碼登陸,簡書就頗有可能會爲了後續業務的須要而擅自保存QQ用戶名及密碼,簡書只要拿到了QQ用戶名密碼就能夠訪問不只僅QQ暱稱、頭像等信息,甚至能夠獲取到QQ用戶的全部通信錄列表。若是簡書的帳號密碼泄露,就會直接影響到QQ數據的安全。這是一個可怕的問題。session

因此OAuth應運而生,來解決這一問題。post

3. OAuth 2.0受權流程

下面咱們就以簡書使用QQ受權登陸爲例,來捋一捋OAuth 2.0的流程。
先來看看OAuth 2.0的流程,以下圖所示:
OAuth 2.0受權流程網站

這裏面主要包含四個角色:ui

  1. Client:須要受權的客戶端,本文中就是【簡書】。
  2. Resource Owner:資源全部者,在本文中你可能會覺得是 QQ,但要想清楚,QQ是屬於我的的,因此在本文中資源全部者是指【QQ用戶】。
  3. Authorization Server:受權服務器,本文中特指【QQ互聯平臺】。
  4. Resource Server:資源服務器,顧名思義,用來專門保存資源的服務器,接受經過訪問令牌進行訪問。本文特指【QQ用戶信息中心】。

3.1. 第一步:引導用戶到受權服務器

聖傑打開簡書網頁,簡書跳轉到登陸界面,要求用戶登陸。但是聖傑未在簡書註冊賬號,因此就點擊了QQ圖標,使用QQ賬號進行集成登陸。跳轉到QQ登陸界面後,QQ要求用戶受權。
這一步中簡書主要作了這樣一件事就是引導用戶到受權服務器。
很顯然【QQ互聯平臺】就是受權服務器。

如何引導?固然是頁面跳轉。
那受權服務器如何知道是簡書過來的認證請求?
固然是傳參。
那須要傳遞哪些參數呢?

  • response_type:表示響應類型,必選項,此處的值固定爲"code";
  • client_id:表示客戶端的ID,用來標誌受權請求的來源,必選項;
  • redirect_uri:成功受權後的回調地址;
  • scope:表示申請的權限範圍,可選項;
  • state:表示客戶端的當前狀態,能夠指定任意值,受權服務器會原封不動地返回這個值。

我們看看簡書實際發送的受權請求Url是:
https://graph.qq.com/oauth2.0/authorize?client_id=100410602 &redirect_uri=http://www.jianshu.com/users/auth/qq_connect/callback &response_type=code &state=bb38108d1aaf567c72da0f1167e87142d0e20cb2bb24ec5a

無圖無真相,我們看看控制檯的網絡監控:

簡書跳轉到QQ登陸的認證請求

如圖所示,除了scope參數外,其餘四個參數均有傳參。
此時你可能惟一對state參數比較迷惑,傳遞一個state參數,受權服務器會原封不動返回,那還幹嗎要傳遞state參數呢?

個人理解是,簡書用一個guid加長版字符串來惟一標識一個受權請求。這樣纔會正確獲取受權服務器返回的受權碼。

這裏你可能會問了,既然我知道了這些參數,我豈不是能夠僞造簡書認證請求,修改redirect_uri參數跳轉到我的的網站,而後不就能夠獲取QQ受權?

跟我同樣太傻太天真,簡書在QQ互聯平臺申請時確定已經預留備案了要跳轉返回的URL。QQ互聯平臺在收到簡書的受權請求時確定會驗證回調Url的。

3.2. 第二步:用戶贊成爲第三方客戶端受權

這一步,對於用戶來講,只須要使用資源全部者(QQ)的用戶名密碼登陸,並贊成受權便可。點擊受權並登陸後,受權服務器首先會post一個請求回服務器進行用戶認證,認證經過後受權服務器會生成一個受權碼,而後服務器根據受權請求的redirect_uri進行跳轉,並返回受權碼code和受權請求中傳遞的state
這裏要注意的是:受權碼有一個短暫的時效

無圖無真相,我們仍是看一下控制檯網絡監控:

用戶受權並登陸

從圖中便可驗證咱們上面所說,最終跳轉回簡書的Url爲:
http://www.jianshu.com/users/auth/qq_connect/callback?code=093B9307E38DC5A2C3AD147B150F2AB3 &state=bb38108d1aaf567c72da0f1167e87142d0e20cb2bb24ec5a
和以前的受權請求URL進行對比,能夠發現redirect_uristate徹底一致。
code=093B9307E38DC5A2C3AD147B150F2AB3就是返回的受權碼。

3.3. 第三步:使用受權碼向受權服務器申請令牌

從這一步開始,對於用戶來講是察覺不到的。簡書後臺默默的在作後續的工做。

簡書拿到QQ互聯平臺返回的受權碼後,須要根據受權碼再次向受權服務器申請令牌(access token)。
到這裏有必要再理清兩個概念:

  • 受權碼(Authorization Code):至關於受權服務器口頭告訴簡書,用戶贊成受權使用他的QQ登陸簡書了。
  • 令牌(Access Token):至關於臨時身份證。

那如何申請令牌呢?
簡書須要後臺發送一個get請求到受權服務器(QQ互聯平臺)。
那要攜帶哪些必要信息呢?
是的,要攜帶如下參數:

  • grant_type:表示受權類型,此處的值固定爲"authorization_code",必選項;
  • client_id:表示從QQ互聯平臺申請到的客戶端ID,用來標誌請求的來源,必選項;
  • client_secret:這個是從QQ互聯平臺申請到的客戶端認證密鑰,機密信息十分重要,必選項;
  • redirect_uri:成功申請到令牌後的回調地址;
  • code:上一步申請到的受權碼。

根據以上信息咱們能夠模擬一個申請AccessToken的請求:
https://graph.qq.com/oauth2.0/token?client_id=100410602 &client_secret=123456jianshu &redirect_uri=http://www.jianshu.com/users/auth/qq_connect/callback &grant_type=authorization_code &code=093B9307E38DC5A2C3AD147B150F2AB3

發送完該請求後,受權服務器驗證經過後就會發放令牌,並返回到簡書後臺,其中應該包含如下信息:

  • access_token:令牌
  • expires_in:access token的有效期,單位爲秒。
  • refresh_token:在受權自動續期步驟中,獲取新的Access_Token時須要提供的參數。

一樣,咱們能夠模擬出一個返回的token:
http://www.jianshu.com/users/auth/qq_connect/callback?access_token=548ADF2D5E1C5E88H4JH15FKUN51F &expires_in=36000&refresh_token=53AD68JH834HHJF9J349FJADF3

這個時候簡書還有一件事情要作,就是把用戶token寫到cookie裏,進行用戶登陸狀態的維持。我們仍是打開控制器驗證一下。

保存token到cookie

從圖中能夠看出簡書把用戶token保存在名爲remember_user_token的cookie裏。
不用打cookie的歪主意了,確定是加密了的。
能夠嘗試下手動把remember_user_token這條cookie刪除,保證刷新界面後須要你從新登陸簡書。

3.4. 第四步:向資源服務器申請資源

有了token,向資源服務器提供的資源接口發送一個get請求不就好了,資源服務器校驗令牌無誤,就會向簡書返回資源(QQ用戶信息)。

一樣我們也來模擬一個使用token請求QQ用戶基本信息資源的URL:
https://graph.qq.com/user/get_user_info?client_id=100410602 &qq=2098769873 &access_token=548ADF2D5E1C5E88H4JH15FKUN51F

到這一步OAuth2.0的流程能夠說是結束了,可是對於簡書來講還有重要的事情要作。那就是:
拿到token、reresh_token和用戶數據這麼重要的東西不存數據庫傻呀?

3.5. 第五步:令牌延期(刷新)

你確定對第四步返回的refresh_token比較好奇。
它是用來對令牌進行延期(刷新)的。爲何會有兩種說法呢,由於可能受權服務器會從新生成一個令牌,也有可能
對過時的令牌進行延期。

好比說,QQ互聯平臺爲了安全性考慮,返回的access_token是有時間限制的,假如用戶某天不想受權了呢,總不能給了個access_token你幾年後還能用吧。咱們上面模擬返回的令牌有效期爲10小時。10小時後,用戶打開瀏覽器逛簡書,瀏覽器中用戶的token對應的cookie已過時。簡書發現瀏覽器沒有攜帶token信息過來,就明白token失效了,須要從新向認證平臺申請受權。若是讓用戶再點擊QQ進行登陸受權,這明顯用戶體驗很差。咋搞呢?refresh_token就派上了用場,能夠直接跳過前面申請受權碼的步驟,當發現token失效了,簡書從瀏覽器攜帶的cookie中的sessionid找到存儲在數據庫中的refresh_token,而後再使用refresh_token進行token續期(刷新)。

那用refresh_token進行token續期須要怎麼作呢?
一樣須要向受權服務器發送一個get請求。
須要哪些參數?

  • grant_type:表示受權類型,此處的值固定爲"refresh_token",必選項;
  • client_id:表示從QQ互聯平臺申請到的客戶端ID,用來標誌請求的來源,必選項;
  • client_secret:這個是從QQ互聯平臺申請到的客戶端認證密鑰,機密信息十分重要,必選項;
  • refresh_token:即申請令牌返回的refresh_token。

根據上述信息,咱們又能夠模擬一個令牌刷新的URL:
https://graph.qq.com/oauth2.0/token?client_id=100410602 &client_secret=123456jianshu &redirect_uri=http://www.jianshu.com/users/auth/qq_connect/callback &grant_type=refresh_token &refresh_token=53AD68JH834HHJF9J349FJADF3
那返回的結果呢?
和第四步返回的結果同樣。

這裏你可能又有疑問了,那既然每次進行令牌延期後都會從新返回一個refresh_token,那豈不是我可使用refresh_token無限延期?
天真如我啊,refresh_token也是有過時時間的。而這個過時時間具體是由受權服務器決定的。
通常來講refresh_token的過時時間要大於access_token的過時時間。只有這樣,access_token過時時,纔可使用refresh_token進行令牌延期(刷新)。

舉個簡單例子:
假設簡書從QQ互聯平臺默認獲取到的access_token的有效期是1天,refresh_token的有效期爲一週。

用戶今天使用QQ登陸受權後,過了兩天再去逛簡書,簡書發現token失效,立馬用refresh_token刷新令牌,默默的完成了受權的延期。
假如用戶隔了兩週再去逛簡書,簡書一覈對,access_tokenrefresh_token全都失效,就只能乖乖引導用戶到受權頁面從新受權,也就是回到OAuth2.0的第一步。

4.0 總結

本文以簡書經過QQ進行受權登陸爲例,對OAuth2.0 的受權流程進行了梳理,但願通讀此文,對你有所幫助。

若是對OAuth2.0有所瞭解的話,你應該明白本文實際上是對OAuth2.0中受權碼模式受權方式的講解。

若是想了解OAuth2.0其餘幾種受權方式,建議參考理解OAuth 2.0 - 阮一峯的網絡日誌

相關文章
相關標籤/搜索