深刻淺出OAuth2.0受權

1、前言javascript

   說到OAuth,先來一段百度到的比較官方的解釋:html

   OAUTH協議爲用戶資源的受權提供了一個安全的、開放而又簡易的標準。與以往的受權方式不一樣之處是OAUTH的受權不會使第三方觸及到用戶的賬號信息(如用戶名與密碼),即第三方無需使用用戶的用戶名與密碼就能夠申請得到該用戶資源的受權,所以OAUTH是安全的。OAuth是Open Authorization的簡寫。java

   說實話,每次見到官方定義的東西 都想git

  (╯' - ')╯︵ ┻━┻ (掀桌子) ┬─┬ ノ( ' - 'ノ) {擺好擺好) (╯°Д°)╯︵ ┻━┻(再掀一次)

   單從字面意思來說,就是Open Authorization,即 開放 受權的意思。官方解釋就是官方,看了幾遍仍然不知道是什麼東西,因此今天博主也根據本身的理解,徹底用白話的方式,不摻雜一點官方語言來解釋下OAuth2.0。用你的親身經歷舉個栗子來講 就是  如今你用到的第三方登陸,好比qq、微信、微博登陸啊,免去了本身註冊帳號的煩惱,實際上這就是OAuth的實現和解決的問題。OAuth也經歷了幾個版本,目前到了2.0版本,也就是咱們常說的OAuth2.0。瀏覽器

2、爲何要有OAuth安全

   是爲了解決問題啊(這不是廢話嗎,出來一個東西,確定是要解決問題的),可是到底解決什麼問題呢,再舉個栗子,不夠吃了。服務器

    

 

  2.1 解決用戶身份認證問題微信

   咱們每當去訪問一個網站的資源的時候,網站都會要求咱們註冊一個帳號,訪問資源的時候登陸帳號來表名咱們的身份,注意,這一步僅僅就是爲了表名你的身份,知道你是個正常的人,而不是一些機器什麼的。你對網站有什麼操做,網站也能很好地監測到。post

      這樣一來,你每當去訪問一個新的網站的時候都要註冊帳號,對用戶來講,麻煩,反正我是不想註冊了,對網站來講,我得記錄你的帳號密碼什麼的,也麻煩。(網站主要目的仍是爲了肯定你的身份)。網站

   那麼問題來,能不能有一種方式,幫網站作認證來表名你是個正常的人,好比能夠找一些比較出名且有權威的大公司大網站來認證一下,如qq,微博什麼的,由於這些大公司已經存儲了你的身份,若是你訪問個人網站可讓大公司來證實一下你的身份,我知道

       你是誰就能夠了,也不用你在個人網站註冊帳號了。這樣一來,你方便,我也方便。因此,OAuth2.0 給出了規範,來解決認證問題。

  2.2 解決第三方網站訪問已有的用戶資源的問題

  如今有這麼一種狀況,既然前邊已經說了,你能夠用你的qq,微博帳號登陸個人網站,那麼我又有其餘非分之想了,我想獲取到你在qq或者微博上暱稱啊來顯示到個人網站上,這樣感受高大上一點,更有甚者,我還想要。。。。emmm ,不是那個了。

    

 

    我還想要其餘東西,我想讀取下你的微博,說說啊什麼的,或者我也想你能在個人網站發表這些東西。這樣豈不是更加高大上。可是這些功能都是qq和微博提供的,我怎麼能獲取到權限呢。

     一個辦法就是 你把你的qq或者微博的帳號和密碼給我,我去登陸,這樣我就能像你同樣操做各類東西。可是你可能就不肯意了,個人帳號密碼都給你了,萬一你把我上邊的小片什麼的都泄漏出來,我還怎麼才朋友裏邊作人,萬一你裝做我搞一些壞事情,我豈不是更要身敗名裂,畢竟這是最高權限啊。還有,我這qq密碼說不定就是其餘各類帳號的密碼,你給我存起來,登陸個人支付寶什麼的,那怎麼行。反正,就是一大堆不行。可是我要獲取你qq和微博上的東西你總得給我吧,因此爲了解決這個問題。OAuth就又出來了,給出來規範,來解決資源受權問題。

  

 

 

3、OAuth解決上述問題的流程

  很明顯,解決上述問題,須要三我的的參與,因此每一個角色之間都得存守必定的約定和規則,這一套規則就是OAuth2.0協議的規範,具體比較官方的規範你們本身去看,本文就是以白話文的方式來說解的,不會有那種官方的條條框框。    

   3.1 第三網站a與qq的協議(只以qq舉例子,其餘都同樣)

   首先qq會對他上邊的用戶權限進行分類,好比說獲取頭像,獲取暱稱,修改暱稱,讀取說說,發表說說之類的權限,都會進行整理,當a網站要獲取那些用戶的權限的時候就會頒發給他相應的權限,固然爲了肯定是a網站來訪問我大企鵝用戶的東西,會要求a網站在qq平臺上註冊一下身份,好比說用client_idclient_sercret來表名a網站應用的身份,若是是b網站 想訪問也要註冊一下,未來qq也會知道是b網站來訪問個人用戶上的資源和權限。

   固然,爲了更加明確和安全,qq還會要求a網站註冊一個地址(redirect_url)或者註冊你須要獲取用戶的那些權限,這樣一來,你來的時候,個人檢查一下redirect_uri是否是你的,當你讓qq的用戶登陸受權成功以後,我也好根據redirect_url跳回去找你啊。

  3.2 用戶和qq的協議 

   用戶來進入a網站,a網站集成了「用qq登陸「登陸的功能,因而用戶點擊用qq登陸,那麼a網站就會帶上client_id去跳轉到qq的受權頁面,qq一看client_id就知道是a網站來的,覈對一下(經過client_id)這個a網站是qq受權過的客戶端,我根據client_id找出a網站能夠獲取的權限,給用戶顯示出來,而後讓用戶選擇你是否贊成a網站獲取你的這些權限。以下圖,這裏我以碼雲 的qq登陸爲例

    

  

  咱們能夠看一下這個頁面的地址:https://graph.qq.com/oauth2.0/show?client_id=101284669&redirect_uri=https%3A%2F%2Fgitee.com%2Fauth%2Fqq_connect%2Fcallback&response_type=code&state=f70af08d82d762a56d844e0b0f1d0b7abafd48127c5b4ee0

  1. 紅色部分是qq的受權地址,綠色部分是碼雲攜帶的參數,很明顯碼雲也向qq註冊了,有client_idredirect_uri兩個重要參數,redirect_uri用戶贊成受權後跳回去,明顯是碼雲的一個地址。

  2. response_type=code 這個參數是OAuth2.0規範,意識是受權方式爲code方式,此處通常填寫固定值code,由於還有其餘受權方式,可是不經常使用,

  3. state=f70af08d82d762a56d844e0b0f1d0b7abafd48127c5b4ee0 這個也是裏邊的規範 狀態參數 值是一個隨機字符串,須要qq原樣返回給碼雲的,置於什麼做用,參考好多資料,都沒有明確指出,你們也只是遵照這個規範(不過確定有用的,如今還不清楚)

  注意:當用戶沒有贊成,OAuth的受權流程就結束了。若是贊成了,才繼續進行。

  因此這一步流程就本就是,你帶着參數向qq的受權頁面跳轉 https://graph.qq.com/oauth2.0/show?&client_id=xxxx&redirect_uri=xxxxx&response_type=code&state=xxxx    紅色部分是你須要填寫的部分。你們能夠參看微信網頁受權就是醬紫的。

  3.3  qq的處理而且返回給a網站

  qq確定是做記錄啊,記錄用戶對碼雲授予了這兩個權限,未來出什麼問題也好作一個憑證啊,萬一你來訛qq一下說,我沒有受權,你怎麼把個人東西給碼雲了。(馬雲:管我什麼事!!)記錄以後,會生成一個code碼,這個code確定是和用戶的受權記錄是一一對應的,這個code碼也是OAuth2.0的規範,而且這個code碼有過時時間,而且只能用一次(置於爲何這麼設計,先插個眼咱們後邊說)。而後你們能夠看後續的流程:

      

 

    3.3.1 首先,qq會給我發個提示:說我已經受權了,表名qq已經知道了,而且也已經留取證據了,你就不可能反咬qq一口了,這也是OAuth2.0設置這一步的目的所在。整個三個角色的態度都會有記錄。

  

    3.3.2 網址以下:https://gitee.com/auth/qq_connect/callback?code=FA0E3CBB6B699D894EE2BFD85691A561&state=f70af08d82d762a56d844e0b0f1d0b7abafd48127c5b4ee0  很明顯能夠看到,這個地址是以前碼雲填寫的redirect_uri的地址,這裏qq跳轉回來,並攜帶上了code碼,說明用戶已經贊成了你的受權,至於state能夠看見仍是原樣返回的。

  3.4 用code獲取access_token

    上一步中,碼雲已經獲取到了code碼,但這個 code 只能代表,用戶容許碼雲從 qq上獲取該用戶的數據,若是我直接拿這個 code 去 qq訪問數據必定會被拒絕,由於任何人均可以持有 code,qq並不知道 code 持有方就是碼雲。

    因此換取access_token的時候就要碼雲本身也攜帶着本身向qq申請的帳號密碼來換取了,這裏邊已經在界面上看不到了,是再跳轉的過程當中默默進行的。

 
  POST https://www.oauth.qq.com/login/oauth/access_token //去請求qq獲取access_token的地址 通常都是post
  params = {
     code: "xxx",
     client_id: "xxx",
     client_secret: "xxx",
     redirect_uri: "https://gitee.com/auth/qq_connect/callback"
  }

  固然若是一切正常,qq會給你返回以下結果

response = {
   access_token: "e72e16c7e42f292c6912e7710c838347ae178b4a"
   scope: "username,headimg" //這個是能夠獲取用戶的權限的範圍,是前邊用戶選擇的部分,這裏舉個例子,表示只容許獲取用戶名和頭像
   token_type: "bearer",
   refresh_token: "e72e1dedfs42f2923112e7710csafgsdaklfhalrf1352371537838347ae178b4a"
}

  3.5 拿着access_token去調用接口

  有access_token了還不隨心所欲啊,獲取個暱稱,獲取的頭像,沒事發個說說,讀個微博啥的。到此OAuth2.0 受權流程基本就結束了。固然這個access_token也是有時間的,通常爲兩個小時。過時的話用返回的refresh_token刷新,由於在請求接口的過程當中傳輸的是access_token且只有兩小時的有效時間,即便是被別人截獲了,損失也不會太大,設計兩個小時的過時是爲了安全考慮的。由於這一步的access_token是訪問接口的惟一憑據,並不會判斷調用者的來源。因此是有可能被非法者截獲搞出一些事情的。

4、總結

 1.關於code碼的問題:

  Q:爲何不直接發放access_token而非要返回一個code碼,而後用code換取access_token,這不是屢次一舉嗎?

  A:首先有這個想法很好,證實你是一個有思想的同窗。當初我也想了好半天。由於qq根據要跳回你的網站才行,而跳轉到你的網站是302重定向,只能是get請求,因此要傳遞給碼雲一些數據信息的時候必須在url中添加參數,若是這時直接把access_token傳遞回來,就會暴露出來,這樣就很不安全,並且是依賴瀏覽器的,因此加了一部分,只把code碼傳回來,這樣即便code被暴露了也沒問題,由於碼雲要在再次用code碼和你的client_id,client_secret來請求qq,qq會作驗證,而這一步就能夠不依賴瀏覽器了,你能夠在服務器用代碼模擬http請求獲取acess_token,這樣就比較安全了。

  Q:爲何code要設計成一次性的?

  A:假若code能無限次用,那麼當用戶在上述狀況下收回了權限,可是因爲code還能用,自己code又關聯了用戶的受權信息,因此碼雲可能再次用code來換取token,這一步並無通過用戶的容許是違法的。因此不能讓code繼續使用。一次code表名了用戶的一次意願,並非終生的意願。置於code爲何要設置有效期,我想應該是你若是獲取了code不使用,qq也不會說一直幫你存着吧。

  另外,當受權碼發放以後,qq確定也確定添加並記錄了用戶當前對哪一個網站發放了受權,這個記錄也爲後來的用戶收回權限作準備,若是用戶要收回權限,直接對相關記錄操做就行。以下,是qq的受權管理:

  

 

 

  2.關於OAuth的標準協議

  有關OAuth的標準協議,各大廠商的內部實現可能不盡相同,有時候傳的參數多多少少會有一點出入,可是大致流程上都是這樣,並且有些必須參數都是同樣的。

  協議的官方內容能夠參考阮一峯老師的 理解OAuth 2.0。另外一篇  簡述 OAuth 2.0 的運做流程 也很不錯。

相關文章
相關標籤/搜索