週末逛簡書,看了一篇寫的極好的文章,點擊大紅心點贊,就直接給我跳轉到登陸界面了,原來點贊是須要登陸的。數據庫
但是沒有我並無簡書帳號,一直使用的QQ的集成登陸。下面有一排社交登陸按鈕,咱們能夠用第三方社交帳號登錄便可。點擊QQ圖標,就給我跳轉到了QQ登陸受權頁面,以下圖:瀏覽器
從圖片上咱們能夠看到主要包括兩個部分,一個是左邊的用戶登陸,一個是右邊告知簡書將獲取哪些權限。輸入QQ帳號和密碼,點擊受權並登陸,就成功登陸到簡書了,併成功獲取到了我QQ的帳號和暱稱,以下圖:安全
簡書集成的社交登陸,大大簡化了咱們的註冊登陸流程,真是一號在手上網無憂啊。
這看似簡單的集成,但背後的技術原理『OAuth2.0』可沒那麼簡單,那咱們廢話很少說,一探究竟吧。服務器
OAuth 2.0是用於受權的行業標準協議。OAuth 2.0取代了在2006年建立的原始OAuth協議上所作的工做。OAuth 2.0專一於客戶端開發人員的簡單性,同時爲Web應用程序,桌面應用程序,手機和客廳設備提供特定的受權流程。cookie
在傳統的client-server認證模型中,客戶端請求訪問服務器上受限的資源(protected resource),須要經過使用資源全部者(resource owner)的憑證在服務器上進行認證。爲了支持第三方應用程序訪問受限資源,資源全部者須要向第三方應用共享其憑證。這就會形成如下問題:網絡
想想這樣一個場景,若是簡書是直接使用QQ用戶名密碼登陸,簡書就頗有可能會爲了後續業務的須要而擅自保存QQ用戶名及密碼,簡書只要拿到了QQ用戶名密碼就能夠訪問不只僅QQ暱稱、頭像等信息,甚至能夠獲取到QQ用戶的全部通信錄列表。若是簡書的帳號密碼泄露,就會直接影響到QQ數據的安全。這是一個可怕的問題。session
因此OAuth應運而生,來解決這一問題。post
下面咱們就以簡書使用QQ受權登陸爲例,來捋一捋OAuth 2.0的流程。
先來看看OAuth 2.0的流程,以下圖所示:
網站
這裏面主要包含四個角色:ui
聖傑打開簡書網頁,簡書跳轉到登陸界面,要求用戶登陸。但是聖傑未在簡書註冊賬號,因此就點擊了QQ圖標,使用QQ賬號進行集成登陸。跳轉到QQ登陸界面後,QQ要求用戶受權。
這一步中簡書主要作了這樣一件事就是引導用戶到受權服務器。
很顯然【QQ互聯平臺】就是受權服務器。
如何引導?固然是頁面跳轉。
那受權服務器如何知道是簡書過來的認證請求?
固然是傳參。
那須要傳遞哪些參數呢?
我們看看簡書實際發送的受權請求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
無圖無真相,我們看看控制檯的網絡監控:
如圖所示,除了scope參數外,其餘四個參數均有傳參。
此時你可能惟一對state參數比較迷惑,傳遞一個state參數,受權服務器會原封不動返回,那還幹嗎要傳遞state參數呢?
個人理解是,簡書用一個guid加長版字符串來惟一標識一個受權請求。這樣纔會正確獲取受權服務器返回的受權碼。
這裏你可能會問了,既然我知道了這些參數,我豈不是能夠僞造簡書認證請求,修改redirect_uri
參數跳轉到我的的網站,而後不就能夠獲取QQ受權?
跟我同樣太傻太天真,簡書在QQ互聯平臺申請時確定已經預留備案了要跳轉返回的URL。QQ互聯平臺在收到簡書的受權請求時確定會驗證回調Url的。
這一步,對於用戶來講,只須要使用資源全部者(QQ)的用戶名密碼登陸,並贊成受權便可。點擊受權並登陸後,受權服務器首先會post一個請求回服務器進行用戶認證,認證經過後受權服務器會生成一個受權碼,而後服務器根據受權請求的redirect_uri
進行跳轉,並返回受權碼code
和受權請求中傳遞的state
。
這裏要注意的是:受權碼有一個短暫的時效
無圖無真相,我們仍是看一下控制檯網絡監控:
從圖中便可驗證咱們上面所說,最終跳轉回簡書的Url爲:
http://www.jianshu.com/users/auth/qq_connect/callback?code=093B9307E38DC5A2C3AD147B150F2AB3 &state=bb38108d1aaf567c72da0f1167e87142d0e20cb2bb24ec5a
和以前的受權請求URL進行對比,能夠發現redirect_uri
、state
徹底一致。
而code=093B9307E38DC5A2C3AD147B150F2AB3
就是返回的受權碼。
從這一步開始,對於用戶來講是察覺不到的。簡書後臺默默的在作後續的工做。
簡書拿到QQ互聯平臺返回的受權碼後,須要根據受權碼再次向受權服務器申請令牌(access token)。
到這裏有必要再理清兩個概念:
那如何申請令牌呢?
簡書須要後臺發送一個get請求到受權服務器(QQ互聯平臺)。
那要攜帶哪些必要信息呢?
是的,要攜帶如下參數:
根據以上信息咱們能夠模擬一個申請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
發送完該請求後,受權服務器驗證經過後就會發放令牌,並返回到簡書後臺,其中應該包含如下信息:
一樣,咱們能夠模擬出一個返回的token:
http://www.jianshu.com/users/auth/qq_connect/callback?access_token=548ADF2D5E1C5E88H4JH15FKUN51F &expires_in=36000&refresh_token=53AD68JH834HHJF9J349FJADF3
這個時候簡書還有一件事情要作,就是把用戶token寫到cookie裏,進行用戶登陸狀態的維持。我們仍是打開控制器驗證一下。
從圖中能夠看出簡書把用戶token保存在名爲remember_user_token
的cookie裏。
不用打cookie的歪主意了,確定是加密了的。
能夠嘗試下手動把remember_user_token
這條cookie刪除,保證刷新界面後須要你從新登陸簡書。
有了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和用戶數據這麼重要的東西不存數據庫傻呀?
你確定對第四步返回的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請求。
須要哪些參數?
根據上述信息,咱們又能夠模擬一個令牌刷新的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_token
、refresh_token
全都失效,就只能乖乖引導用戶到受權頁面從新受權,也就是回到OAuth2.0的第一步。
本文以簡書經過QQ進行受權登陸爲例,對OAuth2.0 的受權流程進行了梳理,但願通讀此文,對你有所幫助。
若是對OAuth2.0有所瞭解的話,你應該明白本文實際上是對OAuth2.0中受權碼模式受權方式的講解。
若是想了解OAuth2.0其餘幾種受權方式,建議參考理解OAuth 2.0 - 阮一峯的網絡日誌。