上一篇文章介紹了 OAuth 2.0 是一種授權機制,主要用來頒發令牌(token)。本文接著介紹頒發令牌的實務操作。

下面我假定,你已經理解了 OAuth 2.0 的含義和設計思想,否則請先閱讀這個系列的上一篇文章。
進入正文之前,插播一則活動消息。
4月22日(週一)到4月29日(下週一),每天晚上八點都有兩小時的免費直播課,體系化介紹高級前端開發知識,網易雲課堂主辦。詳細介紹請看本文結尾,歡迎關注。
RFC 6749
OAuth 2.0 的標準是 RFC 6749 文件。該文件先解釋了 OAuth 是什麼。
OAuth 引入了一個授權層,用來分離兩種不同的角色:客戶端和資源所有者。......資源所有者同意以後,資源服務器可以向客戶端頒發令牌。客戶端通過令牌,去請求數據。
這段話的意思就是,OAuth 的核心就是向第三方應用頒發令牌。然後,RFC 6749 接著寫道:
(由於互聯網有多種場景,)本標準定義了獲得令牌的四種授權方式(authorization grant )。
也就是說,OAuth 2.0 規定了四種獲得令牌的流程。你可以選擇最適合自己的那一種,向第三方應用頒發令牌。下面就是這四種授權方式。
- 授權碼(authorization-code)
- 隱藏式(implicit)
- 密碼式(password):
- 客戶端憑證(client credentials)
注意,不管哪一種授權方式,第三方應用申請令牌之前,都必須先到系統備案,說明自己的身份,然後會拿到兩個身份識別碼:客戶端 ID(client ID)和客戶端密鑰(client secret)。這是為了防止令牌被濫用,沒有備案過的第三方應用,是不會拿到令牌的。
第一種授權方式:授權碼
授權碼(authorization code)方式,指的是第三方應用先申請一個授權碼,然後再用該碼獲取令牌。
這種方式是最常用的流程,安全性也最高,它適用於那些有後端的 Web 應用。授權碼通過前端傳送,令牌則是儲存在後端,而且所有與資源服務器的通信都在後端完成。這樣的前後端分離,可以避免令牌洩漏。
第一步,A 網站提供一個鏈接,用戶點擊後就會跳轉到 B 網站,授權用戶數據給 A 網站使用。下面就是 A 網站跳轉 B 網站的一個示意鏈接。
https://b.com/oauth/authorize? response_type=code& client_id=CLIENT_ID& redirect_uri=CALLBACK_URL& scope=read
上面 URL 中,response_type參數表示要求返回授權碼(code),client_id參數讓 B 知道是誰在請求,redirect_uri參數是 B 接受或拒絕請求後的跳轉網址,scope參數表示要求的授權範圍(這裡是只讀)。

第二步,用戶跳轉後,B 網站會要求用戶登錄,然後詢問是否同意給予 A 網站授權。用戶表示同意,這時 B 網站就會跳回redirect_uri參數指定的網址。跳轉時,會傳回一個授權碼,就像下面這樣。
https://a.com/callback?code=AUTHORIZATION_CODE
上面 URL 中,code參數就是授權碼。

第三步,A 網站拿到授權碼以後,就可以在後端,向 B 網站請求令牌。
https://b.com/oauth/token? client_id=CLIENT_ID& client_secret=CLIENT_SECRET& grant_type=authorization_code& code=AUTHORIZATION_CODE& redirect_uri=CALLBACK_URL
上面 URL 中,client_id參數和client_secret參數用來讓 B 確認 A 的身份(client_secret參數是保密的,因此只能在後端發請求),grant_type參數的值是AUTHORIZATION_CODE,表示採用的授權方式是授權碼,code參數是上一步拿到的授權碼,redirect_uri參數是令牌頒發後的回調網址。

第四步,B 網站收到請求以後,就會頒發令牌。具體做法是向redirect_uri指定的網址,發送一段 JSON 數據。
{ "access_token":"ACCESS_TOKEN", "token_type":"bearer", "expires_in":2592000, "refresh_token":"REFRESH_TOKEN", "scope":"read", "uid":100101, "info":{...} }
上面 JSON 數據中,access_token字段就是令牌,A 網站在後端拿到了。

第二種方式:隱藏式
有些 Web 應用是純前端應用,沒有後端。這時就不能用上面的方式了,必須將令牌儲存在前端。RFC 6749 就規定了第二種方式,允許直接向前端頒發令牌。這種方式沒有授權碼這個中間步驟,所以稱為(授權碼)"隱藏式"(implicit)。
第一步,A 網站提供一個鏈接,要求用戶跳轉到 B 網站,授權用戶數據給 A 網站使用。
https://b.com/oauth/authorize? response_type=token& client_id=CLIENT_ID& redirect_uri=CALLBACK_URL& scope=read
上面 URL 中,response_type參數為token,表示要求直接返回令牌。
第二步,用戶跳轉到 B 網站,登錄後同意給予 A 網站授權。這時,B 網站就會跳回redirect_uri參數指定的跳轉網址,並且把令牌作為 URL 參數,傳給 A 網站。
https://a.com/callback#token=ACCESS_TOKEN
上面 URL 中,token參數就是令牌,A 網站因此直接在前端拿到令牌。
注意,令牌的位置是 URL 錨點(fragment),而不是查詢字符串(querystring),這是因為 OAuth 2.0 允許跳轉網址是 HTTP 協議,因此存在"中間人攻擊"的風險,而瀏覽器跳轉時,錨點不會發到服務器,就減少了洩漏令牌的風險。

這種方式把令牌直接傳給前端,是很不安全的。因此,只能用於一些安全要求不高的場景,並且令牌的有效期必須非常短,通常就是會話期間(session)有效,瀏覽器關掉,令牌就失效了。
第三種方式:密碼式
如果你高度信任某個應用,RFC 6749 也允許用戶把用戶名和密碼,直接告訴該應用。該應用就使用你的密碼,申請令牌,這種方式稱為"密碼式"(password)。
第一步,A 網站要求用戶提供 B 網站的用戶名和密碼。拿到以後,A 就直接向 B 請求令牌。
https://oauth.b.com/token? grant_type=password& username=USERNAME& password=PASSWORD& client_id=CLIENT_ID
上面 URL 中,grant_type參數是授權方式,這裡的password表示"密碼式",username和password是 B 的用戶名和密碼。
第二步,B 網站驗證身份通過後,直接給出令牌。注意,這時不需要跳轉,而是把令牌放在 JSON 數據裡面,作為 HTTP 回應,A 因此拿到令牌。
這種方式需要用戶給出自己的用戶名/密碼,顯然風險很大,因此只適用於其他授權方式都無法採用的情況,而且必須是用戶高度信任的應用。
第四種方式:憑證式
最後一種方式是憑證式(client credentials),適用於沒有前端的命令行應用,即在命令行下請求令牌。
第一步,A 應用在命令行向 B 發出請求。
https://oauth.b.com/token? grant_type=client_credentials& client_id=CLIENT_ID& client_secret=CLIENT_SECRET
上面 URL 中,grant_type參數等於client_credentials表示採用憑證式,client_id和client_secret用來讓 B 確認 A 的身份。
第二步,B 網站驗證通過以後,直接返回令牌。
這種方式給出的令牌,是針對第三方應用的,而不是針對用戶的,即有可能多個用戶共享同一個令牌。
令牌的使用
A 網站拿到令牌以後,就可以向 B 網站的 API 請求數據了。
此時,每個發到 API 的請求,都必須帶有令牌。具體做法是在請求的頭信息,加上一個Authorization字段,令牌就放在這個字段裡面。
curl -H "Authorization: Bearer ACCESS_TOKEN" \ "https://api.b.com"
上面命令中,ACCESS_TOKEN就是拿到的令牌。
更新令牌
令牌的有效期到了,如果讓用戶重新走一遍上面的流程,再申請一個新的令牌,很可能體驗不好,而且也沒有必要。OAuth 2.0 允許用戶自動更新令牌。
具體方法是,B 網站頒發令牌的時候,一次性頒發兩個令牌,一個用於獲取數據,另一個用於獲取新的令牌(refresh token 字段)。令牌到期前,用戶使用 refresh token 發一個請求,去更新令牌。
https://b.com/oauth/token? grant_type=refresh_token& client_id=CLIENT_ID& client_secret=CLIENT_SECRET& refresh_token=REFRESH_TOKEN
上面 URL 中,grant_type參數為refresh_token表示要求更新令牌,client_id參數和client_secret參數用於確認身份,refresh_token參數就是用於更新令牌的令牌。
B 網站驗證通過以後,就會頒發新的令牌。
寫到這裡,頒發令牌的四種方式就介紹完了。下一篇文章會編寫一個真實的 Demo,演示如何通過 OAuth 2.0 向 GitHub 的 API 申請令牌,然後再用令牌獲取數據。
(正文完)
前端高級開發工程師免費直播課
經過多年的發展,前端工程師的地位日益提升,越來越多的人想往前端的方向發展。
但是,市場對前端工程師的要求也越來越高,深入掌握底層技術的高級前端,才能在市場上找到自己立足之地。

為了幫助大家深入瞭解高級前端的學習要點,4月22日(週一)到4月29日(下週一),網易雲課堂推出了前端進階免費直播課。
- 週一(4月22日):《前端不會點 node 怎麼行,node http 模塊詳解》
- 週二(4月23日):《進階面試必備的三大技術題》
- 週三(4月24日):《前端高級工程師核心裝備之柯里化》
- 週四(4月25日):《手把手帶你實現 Vue 的 MVVM》
- 週五(4月26日):《JQuery 原理分析》
- 週六(4月27日):《用迭代器模式讓你的脫離繁重的數據處理 》
- 週日(4月28日):《前端性能--JS的防抖節流實現與案例場景》
- 週一(4月29日):《阿里巴巴 P5 面試題講解》
(更多介紹點擊這裡)
他們邀請了網易資深前端工程師,免費直播分享實戰中的經驗方法。內容涉及網易內部自定組件庫工具分享,前端開發相關知識,深度剖析 JavaScript 等。
這個直播面向前端初、中級開發工程師,每天2個小時,都是一些乾貨分享並且不收費,建議想進階的同學堅持學習。大家可以微信掃碼,添加助教,獲取聽課地址。

(完)












