這是我參與更文挑戰的第22天,活動詳情查看: 更文挑戰前端
OAuth 2.0 是一種 安全協議,今天聊一聊微信小程序的常規登陸受權流程和背後的原理。ios
登陸認證是一個完整應用必備的模塊,小程序和微信是一種相似應用與平臺的關係,小程序屬於微信公衆平臺,同一個平臺下還有微信公衆號:web
在技術角度上,小程序與微信的關係比公衆號更密切,由於公衆號的文章本質上是一個 H5 網頁,對微信底層的依賴比小程序弱;編程
從產品角度上,兩者與微信的關係一致,都是運行在微信平臺上的第三方應用。小程序
根據微信官方文檔描述,開發者獲取用戶登陸態信息的過程正是一個受權碼的許可流程:首先,開發者經過 wx.login(Object object) 方法獲取到登陸憑證 code 值,這一步的流程是在小程序內部經過調用微信提供的 SDK 實現;而後,再經過該 code 值換取用戶的 session_key 等信息,也就是官方文檔的auth.code2Session 方法,同時該方法也是被強烈建議經過開發者的後端服務來調用的。後端
這個過程並無使用到瀏覽器,但確實按照受權碼許可的思想走了一個完整的受權碼許可流程。也就是說,先經過小程序前端獲取到 code 值,再經過小程序的後端服務使用 code 值換取 session_key 等信息,只不過是訪問令牌 access_token 的值被換成了 session_key。微信小程序
既然小程序是微信平臺的第三方應用,那麼在接入微信登陸時就要嚴格遵照官方的接入規範。而在互聯網技術領域,對於支持第三方應用接入的平臺,在登陸受權上有一套標準的技術規範:OAuth 2.0。瀏覽器
開始以前,你仍是要先回想下生活場景,用戶小明登陸某電商小程序,而且想要訪問本身雙十一的訂單,這時候第三方軟件玄武受權訂單數據的整個流程。咱們說第三方軟件先要讓小明去微信商家開放平臺那裏給它受權數據,那這裏是否是你以爲很奇怪?安全
你總不能說,「嘿,微信平臺,你把數據給第三方軟件玄武(你想象成某快遞軟件也能夠)用吧」,那微信平臺確定會回覆說,「小明,玄武是誰啊,沒在咱家備過案,我不能給他,萬一是騙子呢?」對吧,你想一想是否是這個邏輯。因此,受權這個大動做的前提,確定是玄武要去微信平臺那裏「備案」,也就是註冊。註冊完後,微信商家開放平臺就會給玄武軟件 app_id 和app_secret 等信息,以方便後面受權時的各類身份校驗。同時,註冊的時候,第三方軟件也會請求受保護資源的可訪問範圍。好比,玄武可否獲取小明店鋪 3 個月之前的訂單,可否獲取每條訂單的全部字段信息等等。這個權限範圍,就是 scope。 備完案以後,我們接着繼續前進。小明過來讓平臺把他的訂單數據給第三方軟件玄武,平臺咔咔一查,對了下暗號,發現第三方軟件玄武是合法的,因而就要推動下一步了。 在受權碼許可類型中,受權服務的工做,能夠劃分爲兩大部分,一個是頒發受權碼 code,一個是頒發訪問令牌 access_token。服務器
經過簡單的受權碼場景能夠了解到,微信爲宿主的小程序使用微信登陸是一件理所固然的事兒,但其實小程序並無強制要求只能使用微信登陸,也能夠跟 Web 網站同樣使用帳戶密碼模式、郵箱、手機號等登陸方式。既然如此,爲何咱們必定要使用微信登陸呢?由於除了便利性之外,微信登陸更重要的優點是整合了微信龐大的生態系統,以及對於產品策略的加持。
小程序是微信平臺上的一款第三方應用,在登陸方式的選擇上有很高的自由度,微信登陸僅僅是其中一個選擇,你徹底可使用跟其餘應用(網站、App)同樣的登陸方式,好比手機號、郵箱、用戶名密碼等。 事實上微信小程序僅僅是衆多Web產品矩陣中的一個應用端,同時存在的還有網站、App 等應用端。從生態系統的角度上,相對於其餘應用端,以微信爲入口的小程序最大的優點是擁有微信完善的生態系統,用戶使用微信登陸後可使用微信提供給小程序的各類平臺級能力,好比訂閱消息、微信支付、小程序直播、音視頻對話等。
從用戶體驗的角度上,使用微信登陸,用戶可以很大程度上下降登陸的複雜程度,無形中提升用戶的登陸成本和用戶體驗。從產品策略的角度上,使用微信登陸小程序可以根據用戶的來源,制定特殊的產品策略,好比對於小程序的用戶發放商家優惠券、訂閱消息等等。
因此由此得出小程序使用微信登陸的三個主要優點:
這說明了小程序接入微信登陸的必要性,那它的登陸流程是什麼呢?以下圖:
整個登陸流程中描述了三種角色和六個術語,瞭解它們的定位和做用,是理解小程序登陸流程的基礎。
登陸流程裏的三個角色,客戶端在整個登陸流程中主要承擔兩種行爲:
不過客戶端的全部信息和網絡請求幾乎都是能夠被破解或攔截的,因此出於安全的考慮,小程序登陸流程中的一些接口被限制不能在客戶端中直接調用,而是須要在服務端發起,開發者服務的工做即是處理這些安全敏感的網絡請求,體現爲上圖中使用code 獲取 openid 和 session_key的請求,這個請求使用了微信提供的 auth.code2Session 接口。
而微信接口服務的工做對於開發者來講是不透明的,你須要作的僅僅是根據接口的規範,組裝網絡請求發送給它,而後根據返回的接口執行分發邏輯。微信服務器會驗證網絡請求的合法性,對於合法請求下發密鑰 session_key 和用戶 openid。
它是在小程序內經過 API wx.login 獲取的,而後經過 HTTP 請求發送給開發者服務器。code 的做用體如今「臨時」兩字上,它的有效期限僅有 5 分鐘,而且僅可以使用一次(即請求一次 auth.code2Session 接口)。
每一個微信小程序在註冊時就會生成一個惟一的appid,這個 ID 標記了小程序的惟一性,等同於網站的URL(通過備案的)、App 的包名等標記應用惟一性的信息。
它是小程序的密鑰,能夠在微信公衆平臺的後臺管理系統中獲取。appsecret 是很是私密的信息,因此微信在制定小程序登陸的流程時,將攜帶此信息的網絡請求限制在只能經過開發者服務器發送給微信接口服務,這樣對於客戶端來講是不可見的,進而下降了被泄露的可能性。與appid 不一樣的是,appsecret 能夠被重置,但每次重置以後,歷史的 appsecret 便會失效。
微信對於用戶 openid 的定義是:微信號在某個應用程序中的惟一 ID。這裏的「某個應用程序」(好比蘭蔻小程序)指的是小程序、公衆號、接入開放平臺的應用。微信生態中目前有公衆平臺和開放平臺兩種,其中公衆平臺又細分爲小程序和公衆號,開放平臺能夠接入網站、移動應用等。同一個微信號在不一樣的應用程序中有不一樣的 openid。
在微信生態下另外有一個標記微信號的惟一 ID:UnionId。這個 ID 跟應用程序無關。因此,能夠簡單地理解爲 openid 是 UnionId 與 appid 綜合加密後的結果。
UnionId 一般用來關聯在不一樣應用程序中各個 openid ,好比同一個微信號在小程序和公衆號內須要配置一樣的權限,僅經過 openid 沒法實現,便須要獲取此微信號的 UnionId。
session_key 是對用戶數據進行加密簽名的密鑰,微信服務器使用它將用戶的數據進行加密和解密。你能夠簡單地將 session_key 理解爲獲取用戶數據的「綠卡」,登陸以後全部涉及訪問微信服務器的請求通常都須要帶上它,微信服務器會校驗 session_key 的合法性。
其實到這一步(即拿到了 openid 和 session_key)已經完成了小程序的登陸流程,但對於一個應用程序來講,用戶進行登陸操做應該是「一勞永逸」的,即登陸過一次以後在必定時間以內的後續操做都不須要再次登陸,用技術語言描述就是應該保存用戶的登陸態。這個時候就須要用到接下來的一個術語:token。
登陸態是個邏輯詞彙,token 能夠理解爲登陸態的具象化、數據化。在小程序的登陸流程圖中,你能夠看出,token是由開發者服務器建立的一個字符串,並且須要跟 openid 和 session_key 相關聯。其實這裏並非強制關聯 openid,由於 openid 並不算是私密信息,能夠放心地下發到客戶端(即小程序)。可是 session_key 是很是私密的信息,一旦泄露有很大的安全隱患,因此強烈建議不要把它下發到客戶端。
在獲取到 openid 和 session_key 以後,開發者服務器建立一個 token,而後與 openid 和session_key 進行關聯,具體的方法根據服務器編程語言的不一樣有多種實現方案。我們以JavaScript 語言做爲示例,能夠建立一個對象,對象的 key 是 token 的值,value 是一個包含 openid 和 session_key 的對象,以下:
{
"token_1": {
"openid": "獲取到的openid 1",
"session_key": "獲取到的session_key 1"
},
"token_2": {
"openid": "獲取到的openid 2",
"session_key": "獲取到的session_key 2"
},
}
複製代碼
關聯完成以後開發者服務器將 token下發到客戶端,客戶端保存在本地,後續的全部請求均須要攜帶此 token,攜帶的方法並無既定的規範,能夠經過 URL Query、HTTP Body、Header 等,但一般建議經過 Header 傳遞,這樣相對來講更安全一些。
OAuth 2.0 這種受權協議,就是保證第三方(軟件)只有在得到受權以後,才能夠進一步訪問受權者的數據。這裏的受權服務和受保護資源服務都是微信商家開放平臺(不過應該是對應受權服務和訂單服務)、客戶端(第三方軟件)對應的是玄武軟件、資源的擁有者小明經過OAuth2.0的受權碼模式受權後,第三方軟件玄武能夠訪問對應的訂單,
我們先思考一個問題:小程序登陸以後若是須要訪問用戶的數據(好比暱稱、地域、性別等)須要獲得誰的受權?是微信?仍是用戶? 答案是用戶。用戶的數據雖然存放在微信的服務器之上,可是這些數據的全部權屬於用戶本身,而不是微信。這裏其實引出了 OAuth 2.0 規範中的兩個基本概念。
而小程序在獲取用戶數據中的角色是做爲微信平臺的第三方應用程序,在 OAuth 2.0 規範中的術語爲 Third-party application。 除了以上三種角色以外,OAuth 2.0規範中還有另外三種角色:
小程序依託於微信提供的底層技術平臺,微信爲小程序提供了與用戶(即Resource Owner)溝通的工具,它在 OAuth 2.0 規範中的角色被稱爲 User Agent(用戶代理)。 微信服務器不只僅做爲 Resource Server 保存用戶數據,同時在登陸受權過程當中又提供了HTTP服務以及受權認證功能,這兩個功能的角色在 OAuth 2.0 規範中分別被稱爲 HTTP Service(HTTP服務提供商)和Authorization server(認證服務器)。
以上即是 OAuth 2.0 規範中的全部角色,爲了增強了解,咱們再梳理一遍:
Resource Owner
(資源全部者):在小程序場景下表明小程序的用戶。
Resource Server
(資源服務器,即存放用戶數據、資源的服務器):在小程序場景下這個角色由微信服務器承擔。
Third-party application
(第三方應用程序/又稱客戶端):在小程序場景下表明小程序。
User Agent
(用戶代理):在小程序場景下表明微信。
Authorization server
(認證服務器):在小程序場景下,這個角色由微信服務器承擔。
HTTP Service
(HTTP 服務提供商):在小程序場景下,這個角色由微信服務器承擔。
看到這裏,可能以爲 OAuth 2.0 規範中的角色與小程序登陸流程中角色不同?
其實,小程序登陸流程中的三個角色是按照實體劃分的,而 OAuth 2.0 規範的角色是按照功能劃分的,同一個實體能夠擔任一種或多種功能。 在小程序登陸流程中的 3 個實體角色中,微信同時擔任 Third-party application 和User Agent 的功能;微信服務器同時擔任 Resource Server、Authorization server 和HTTP Service 的功能;開發者服務器比較特殊,它即擔任 HTTP Service 的功能,同時在認證流程中因爲須要轉發和關聯 token,因此也充當了客戶端的一部分功能。
好比你向鄰居借了衣架忘了還,某天鄰居着急使用所纔打電話向你要回,不巧的是你正在外地出差家裏沒人。但好在你家的門鎖是智能門鎖,你能夠將密碼告訴鄰居讓他本身去你家裏取。可是你本着「防人之心不可無」的心理,擔憂鄰居是否會趁機記下甚至修改你家的門鎖密碼。左右爲難的時候,你忽然想起來你家的智能門鎖能夠建立臨時密碼,這種臨時密碼只能在 10 分鐘以內有效,並且沒有修改本來密碼的權限。因此,最終你在手機上建立了一個臨時智能門鎖的密碼發給你的鄰居。
OAuth 2.0 規範要解決的問題與上面提到的這個現實案例很是類似,簡單歸納就是:OAuth 2.0是一個受權機制,資源全部者告訴認證服務器,臨時授予某個第三方應用訪問資源服務器獲取資源的權限,認證服務器給第三方應用頒發一個臨時令牌,擁有這個令牌即可以獲取資源數據,一旦令牌過時或失效便收回權限。
OAuth 2.0 規範中的令牌與小程序登陸場景下的 token 做用是一致的,只不過 OAuth 規範只定義了令牌的做用,並無限制它的具體使用方法,微信把 token 與 session_key 相關聯,開發者服務器經過 token 取到 session_key 進而解密用戶資源數據。
簡單梳理一下,受權服務的核心就是,先頒發受權碼 code 值,再頒發訪問令牌 access_token 值。在頒發訪問令牌的同時還會頒發刷新令牌 refresh_token 值,這種機制能夠在無須用戶參與的狀況下用於生成新的訪問令牌。正如咱們講到的小明使用第三方軟件玄武的例子,當訪問令牌過時的時候,刷新令牌的存在能夠大大提升小明使用第三方軟件玄武的體驗。
受權還要有受權範圍,不能讓第三方軟件得到比註冊時權限範圍還大的受權,也不能得到超出了用戶受權的權限範圍,始終確保最小權限安全原則。好比,小明只爲第三方軟件玄武授予了獲取當天訂單的權限,那麼第三方軟件玄武就不能訪問小明店鋪裏面的歷史訂單數據。
刷新令牌有過時時間嗎,會一直有效嗎?答案是刷新令牌也有有效期,若access_token未超時,那麼進行refresh_token有兩種方式,(1)不會改變access_token,但超時時間會刷新,至關於續期access_token(2)更新access_token的值,咱們建議【統一更新access_token的值】。
無限續期會增長access被破解的風險,延期access_token並非一個最好的方式,儘管有的開放平臺是這麼作的。不過當刷新令牌也過時了,只能從新登陸再受權。
在 OAuth 2.0 的體系裏面有 4 種角色,按照官方的稱呼它們分別是資源擁有者、客戶端、受權服務和受保護資源。咱們不難發現,OAuth 2.0 受權的核心就是頒發訪問令牌、使用訪問令牌。 OAuth 2.0 的核心是受權許可,更進一步說就是令牌機制。也就是說,像第三方軟件玄武這樣的第三方軟件只有拿到了微信商家開放平臺頒發的訪問令牌,也就是獲得了受權許可,而後才能夠表明用戶訪問他們的數據。
1.互聯網中全部的受保護資源,幾乎都是以 Web API 的形式來提供訪問的,好比極客時間App 要獲取用戶的頭像、暱稱,第三方軟件玄武要獲取用戶的店鋪訂單,咱們說OAuth 2.0 與安全相關,是用來保護 Web API 的。另外,第三方軟件經過OAuth 2.0 取得訪問權限以後,用戶便把這些權限委託給了第三方軟件,咱們說 OAuth 2.0 是一種委託協議,也沒問題。
2.也正由於像玄武這樣的第三方軟件,每次都是用訪問令牌而不是用戶名和密碼來請求用戶的數據,才大大減小了安全風險上的「攻擊面」。否則,咱們試想一下,每次都帶着用戶名和密碼來訪問數量衆多的 Web API ,是否是增長了這個「攻擊面」。所以,咱們說 OAuth 2.0 的核心,就是頒發訪問令牌和使用訪問令牌。
受權服務就是負責頒發訪問令牌的服務。更進一步地講,OAuth 2.0 的核心是受權服務,而受權服務的核心就是令牌。頒發受權碼 code 的流程以下:
在這個過程當中,受權服務須要完成兩部分工做,分別是準備工做和生成受權碼 code。好比在點擊微信小程序受權Button時,實際上已經作了一系列動做。包括驗證基本信息、驗證權限範圍(第一次)和生成受權請求頁面這三步等等。
驗證基本信息,包括對第三方軟件合法性和回調地址合法性的校驗。在 Web 瀏覽器環境下發放 code 的整個請求過程,都是瀏覽器經過前端通訊來完成,這就意味着全部信息都有被冒充的風險。所以,受權服務必須對第三方軟件的存在性作判斷。一樣,回調地址也是能夠被僞造的。好比,不法分子將其假裝成釣魚頁面,或者是帶有惡意攻擊性的軟件下載頁面。所以從安全上考慮,受權服務須要對回調地址作基本的校驗。在受權服務的程序中,這兩步驗證經過後,就會生成或者響應一個頁面(屬於受權服務器上的頁面),以提示用戶進行受權。
既然是受權,就會涉及範圍。好比,咱們使用微信登陸第三方軟件的時候,會看到微信提示咱們,第三方軟件能夠得到你的暱稱、頭像、性別、地理位置等。若是你不想讓第三方軟件獲取你的某個信息,那麼能夠不選擇這一項。
這就意味着,咱們須要對第三方軟件玄武傳過來的 scope 參數,與第三方軟件玄武註冊時申請的權限範圍作比對。若是請求過來的權限範圍大於註冊時的範圍,就須要做出越權提示。記住,此刻是第一次權限校驗。
當小程序或web頁面出現不一樣權限的button受權彈窗時,用戶能夠選擇縮小這個權限範圍,好比僅授予獲取 地理位置或手機號 的權限。至此,頒發受權碼 code 的準備工做就完成了。你要注意哈,我一直強調說這也是準備工做,由於當用戶點擊受權按鈕「肯定」後,纔會生成受權碼 code 值和訪問令牌acces_token 值,「一切才真正開始」。 在上面的準備過程當中,咱們忽略了小明登陸的過程,但只有用戶登陸了才能夠對第三方軟件進行受權,受權服務纔可以得到用戶信息並最終生成 code 和app_id(第三方軟件的應用標識) + user(資源擁有者標識)之間的對應關係。小明點擊「贊成」按鈕以後,生成受權碼 code 的流程就正式開始了,主要包括驗證權限範圍(第二次)、處理受權請求生成受權碼 code 和重定向至第三方軟件這三大步。
在步驟二中,生成受權頁面以前受權服務進行的第一次校驗,是對比第三方軟件玄武請求過來的權限範圍 scope 和註冊時的權限作的比對。這裏爲何又要校驗一次呢?由於這至關於一次用戶的輸入權限。用戶選擇了必定的權限範圍給到受權服務,對於權限的校驗咱們要重視對待,凡是輸入性數據都會涉及到合法性檢查。另外,這也是要求咱們養成一種在服務端對輸入數據的請求,都儘量作一次合法性校驗的好習慣。
當用戶贊成受權以後,受權服務會校驗響應類型 response_type 的值。response_type 有code 和 token 兩種類型的值。在這裏,咱們是用受權碼流程來舉例的,所以代碼要驗證response_type 的值是否爲 code。
在受權服務中,須要將生成的受權碼 code 值與 app_id、user 進行關係映射。也就是說,一個受權碼 code,表示某一個用戶給某一個第三方軟件進行受權,好比小明給第三方軟件玄武進行的受權。同時,咱們須要將 code 值和這種映射關係保存起來,以便在生成訪問令牌 access_token 時使用。
在生成了受權碼 code 以後,咱們也按照上面所述綁定了響應的映射關係。這時,你還記得我以前講到的受權碼是臨時的、一次性憑證嗎?所以,咱們還須要爲 code 設置一個有效期。 OAuth 2.0 規範建議受權碼 code 值有效期爲 10 分鐘,而且一個受權碼 code 只能被使用一次。不過根據經驗呢,在生產環境中 code 的有效期通常不會超過 5 分鐘。同時,受權服務還須要將生成的受權碼 code 跟已經受權的權限範圍 rscope 進行綁定並存儲,以便後續頒發訪問令牌時,咱們可以經過 code 值取出受權範圍並與訪問令牌綁定。由於第三方軟件最終是經過訪問令牌來請求受保護資源的。
生成受權碼 code 值以後,受權服務須要將該 code 值告知第三方軟件玄武。開始時咱們提到,頒發受權碼 code 是經過前端通訊完成的,所以這裏採用重定向的方式。到此,頒發受權碼 code 的流程所有完成。當第三方軟件玄武獲取到受權碼 code 值之後,就能夠開始請求訪問令牌 access_token 的值。
當第三方軟件玄武拿着受權碼 code 來請求的時候,受權服務須要爲之生成最終的請求訪問令牌。這個過程主要包括驗證第三方軟件是否存在、驗證 code 值是否合法和生成 access_token 值這三大步。
此時,接收到的 grant_type 的類型爲 authorization_code。因爲頒發訪問令牌是經過後端通訊完成的,因此這裏除了要校驗 app_id 外,還要校驗 app_secret。
String grantType = request.getParameter("grant_type");
if("authorization_code".equals(grantType)){
}
if (!appMap.get("app_id").equals(appId)) {
//app_id不存在
}
if (!appMap.get("app_secret").equals(appSecret)) {
//app_secret不合法
}
複製代碼
受權服務在頒發受權碼 code 的階段已經將 code 值存儲了起來,此時對比從 request 中 接收到的 code 值和從存儲中取出來的 code 值。這裏咱們必定要記住,確認過受權碼 code 值有效之後,應該馬上從存儲中刪除當前的 code 值,以防止第三方軟件惡意使用一個失竊的受權碼 code 值來請求受權服務。
關於按照什麼規則來生成訪問令牌 access_token 的值,OAuth 2.0 規範中並無明確規定,但必須符合三個原則:惟一性、不連續性、不可猜性。和受權碼 code 值同樣,咱們須要將訪問令牌 access_token 值存儲起來,並將其與第三方軟件的應用標識 app_id 和資源擁有者標識 user 進行關係映射。也就是說,一個訪問令牌 access_token 表示某一個用戶給某一個第三方軟件進行受權。 同時,受權服務還須要將受權範圍跟訪問令牌 access_token 作綁定。最後,還須要爲該訪問令牌設置一個過時時間 expires_in,好比 1 天。
正由於 OAuth 2.0 規範沒有約束訪問令牌內容的生成規則,因此咱們有更高的自由度。咱們既能夠像 Demo 中那樣生成一個 UUID 形式的數據存儲起來,讓受權服務和受保護資源共享該數據;也能夠將一些必要的信息經過結構化的處理放入令牌自己。咱們將包含了一些信息的令牌,稱爲結構化令牌,簡稱 JWT。
至此,受權碼許可類型下受權服務的兩大主要過程,也就是頒發受權碼和頒發訪問令牌的流程。接下來,你在分析微信登陸的第三方軟件的時候,就會明白很快背後的原理了。同時,你在本身搭建一個受權服務流程時,也會更加駕輕就熟。這一切的緣由,都在於頒發受權碼和頒發訪問令牌,就是受權服務的核心。
爲何必定要引入刷新令牌的概念。好比用戶給微信小程序某電商應用受權之後,正在愉快地處理他的雙十一的訂單數據,結果沒過多久,忽然間受權過時,第三方電商應用再次讓用戶進行受權。此刻用戶的體驗是很是糟糕的。爲此,OAuth 2.0 中引入了刷新令牌的概念,也就是刷新訪問令牌 access_token 的值。這就意味着,有了刷新令牌,用戶在必定期限內無需從新點擊受權按鈕,就能夠繼續使用第三方軟件。
刷新令牌也是給第三方軟件使用的,一樣須要遵循先頒發再使用的原則。所以,咱們仍是從頒發和使用兩個環節來學習刷新令牌。
其實,頒發刷新令牌和頒發訪問令牌是一塊兒實現的,都是在過程二的步驟三生成訪問令牌access_token 中生成的。也就是說,第三方軟件獲得一個訪問令牌的同時,也會獲得一個刷新令牌。 看到這裏你可能要問了,爲何要一塊兒生成訪問令牌和刷新令牌呢?
其實,這就回到了刷新令牌的做用上了。刷新令牌存在的初衷是,在訪問令牌失效的狀況下,爲了避免讓用戶頻繁手動受權,用來經過系統從新請求生成一個新的訪問令牌。那麼,若是訪問令牌失效了,而「身邊」又沒有一個刷新令牌可用,豈不是又要麻煩用戶進行手動受權了。因此,它必須得和訪問令牌一塊兒生成。
在 OAuth 2.0 規範中,刷新令牌是一種特殊的受權許可類型,是嵌入在受權碼許可類型下的一種特殊許可類型。在受權服務的代碼裏,當咱們接收到這種受權許可請求的時候,會先比較 grant_type 和 refresh_token 的值,而後作下一步處理。
此時請求中的 grant_type 值爲 refresh_token。和頒發訪問令牌前的驗證流程同樣,這裏咱們也須要驗證第三方軟件是否存在。須要注意的是,這裏須要同時驗證刷新令牌是否存在,目的就是要保證傳過來的刷新令牌的合法性。
另外,咱們還須要驗證刷新令牌是否屬於該第三方軟件。受權服務是將頒發的刷新令牌與第三方軟件、當時的受權用戶綁定在一塊兒的,所以這裏須要判斷該刷新令牌的歸屬合法性。 須要注意,一個刷新令牌被使用之後,受權服務須要將其廢棄,並從新頒發一個刷新令牌。
生成訪問令牌的處理流程,與頒發訪問令牌環節的生成流程是一致的。受權服務會將新的訪問令牌和新的刷新令牌,一塊兒返回給第三方軟件。 不少電商小程序都會設置token失效自動刷新請求,作法以下:
/** 刷新Token, 默認只刷新一次 */
function refreshToken(params: AxiosRequestConfig) {
return promisify(wx.login)()
.then((res: WechatMiniprogram.LoginSuccessCallbackResult) => promisify(wx.request)({
url: config.loginUrl + res.code
}))
.then((res: any) => {
wx.setStorageSync('token', res.data.jwtString)
return refreshRequest(params)
}, (err: any) => {
return Promise.reject(err)
})
}
/** 從新發起請求 */
function refreshRequest(config: AxiosRequestConfig) {
return promisify(wx.request)({
url: config.url,
header: Object.assign({}, config.headers, {
'Authorization': wx.getStorageSync('token')
}),
data: config.data,
method: config.method,
timeout: config.timeout
}).then((res: any) => {
const response: AxiosResponse = {
data: res.data,
status: res.statusCode,
statusText: res.errMsg,
headers: res.header,
config: config,
cookies: res.cookies
}
return response
}, (err: any) => {
return Promise.reject(err)
})
}
複製代碼
受權碼許可流程有兩種通訊方式。一種是前端通訊,由於它經過瀏覽器促成了受權碼的交互流程,好比微信商家開放平臺的受權服務生成受權碼發送到瀏覽器,第三方軟件玄武從瀏覽器獲取受權碼。正由於獲取受權碼的時候第三方軟件玄武和受權服務並無發生直接的聯繫,也叫作間接通訊。另一種是後端通訊,在第三方軟件玄武獲取到受權碼以後,在後端服務直接發起換取訪問令牌的請求,也叫作直接通訊。 爲何要用受權碼換取token,而不是直接獲取token?
直接獲取token的場景是有的,客戶端憑據許可類型就是這樣的使用場景,受權碼許可類型是OAuth 2.0 最安全 最完備的許可類型。 以Web場景爲例,第一次用戶是跟第三方軟件創建的「聯繫」,三方軟件要把用戶引導到平臺一方去受權,這個時候用戶實際上跟三方軟件就失去了「聯繫」,平臺若是這個時候直接把令牌給了三方軟件,由於沒了「聯繫」,三方軟件就不能很方便的告訴用戶。
在 OAuth 2.0 中,訪問令牌被要求有極高的安全保密性,所以咱們不能讓它暴露在瀏覽器上面,只能經過第三方軟件(好比玄武)的後端服務來獲取和使用,以最大限度地保障訪問令牌的安全性。正由於訪問令牌的這種安全要求特性,當須要前端通訊,好比瀏覽器上面的流轉的時候,OAuth 2.0 才又提供了一個臨時的憑證:受權碼。經過受權碼的方式,可讓用戶小明在受權服務上給第三方軟件玄武受權以後,還能從新回到第三方軟件玄武的操做頁面上。
從受權碼許可流程中就能夠看出來,它完美地將 OAuth 2.0 的 4 個角色組織了起來,並保證了它們之間的順暢通訊。它提出的這種結構和思想均可以被遷移到其餘環境或者協議上,好比在微信小程序中使用受權碼許可。
不過,也正是由於有了受權碼的參與,才使得受權碼許可要比其餘受權許可類型,在受權的流程上多出了好多步驟,讓受權碼許可類型成爲了 OAuth 2.0 體系中迄今流程最完備、安全性最高的受權流程。
《OAuth 2.0實戰課》王新棟老師
《雲原生微信小程序開發實戰》周俊鵬