身份認證系統Oauth2介紹

先看兩張截圖

  • 第一張圖是登陸微博時使用第三方QQ登陸,跳轉至QQ的登陸授權頁面。
https://graph.qq.com/oauth2.0/show?which=Login&display=pc&client_id=101019034&response_type=code&scope=get_info%2Cget_user_info&redirect_uri=https%3A%2F%2Fpassport.weibo.com%2Fothersitebind%2Fbind%3Fsite%3Dqq%26state%3DCODE-yf-1JIxhr-24OJQs-OUjVyPzzFPSiItg05a03f%26bentry%3Dminiblog%26wl%3D&display=

在這裏插入圖片描述

  • 第二張是打開知乎小程序後登陸的提示。
    在這裏插入圖片描述

什麼是Oauth2

OAuth 2.0(open authorization)是一個關於授權的開放網絡標準,是目前最流行的授權機制,一般用來授權第三方應用,獲取用戶數據。

快遞員問題

首先假設我住在一個小區,小區是有門禁系統,進入小區需要輸入密碼。
然後我又經常網購和外賣,每天都有快遞員來送貨。我不想每次都到門口拿快遞,所以必須想一個辦法,讓快遞員通過門禁系統,進入小區。
如果我直接把自己的密碼告訴快遞員,他就擁有了與我同樣的權限,這樣好像不太合適。萬一我想取消他進入小區的權力,也很麻煩,我自己的密碼也得跟着改了,還得通知其他的快遞員。

有沒有一種辦法,讓快遞員能夠自由進入小區,又不必知道小區居民的密碼,而且他的唯一權限就是送貨,其他需要密碼的場合,他都沒有權限?

於是,我設計了一套授權機制。
第一步,門禁系統的密碼輸入器下面,增加一個按鈕,叫做"獲取授權"。快遞員需要首先按這個按鈕,去申請授權。

第二步,他按下按鈕以後,屋主(也就是我)的手機就會跳出對話框:有人正在要求授權。系統還會顯示該快遞員的姓名、工號和所屬的快遞公司。

我確認請求屬實,就點擊按鈕,告訴門禁系統,我同意給予他進入小區的授權。

第三步,門禁系統得到我的確認以後,向快遞員顯示一個進入小區的令牌(access token)。令牌就是類似密碼的一串數字,只在短期內(比如七天)有效。

第四步,快遞員向門禁系統輸入令牌,進入小區。

有人可能會問,爲什麼不是遠程爲快遞員開門,而要爲他單獨生成一個令牌?這是因爲快遞員可能每天都會來送貨,第二天他還可以複用這個令牌。另外,有的小區有多重門禁,快遞員可以使用同一個令牌通過它們。

互聯網場景

我們把上面的例子搬到互聯網,就是 OAuth 的設計了。

首先,居民小區就是儲存用戶數據的網絡服務。比如,微信儲存了我的好友信息,獲取這些信息,就必須經過微信的"門禁系統"。

其次,快遞員(或者說快遞公司)就是第三方應用,想要穿過門禁系統,進入小區。

最後,我就是用戶本人,同意授權第三方應用進入小區,獲取我的數據。

簡單說,OAuth 就是一種授權機制。數據的所有者告訴系統,同意授權第三方應用進入系統,獲取這些數據。系統從而產生一個短期的進入令牌(token),用來代替密碼,供第三方應用使用。

令牌與密碼

令牌(token)與密碼(password)的作用是一樣的,都可以進入系統,但是有三點差異。

(1)令牌是短期的,到期會自動失效,用戶自己無法修改。密碼一般長期有效,用戶不修改,就不會發生變化。

(2)令牌可以被數據所有者撤銷,會立即失效。以上例而言,屋主可以隨時取消快遞員的令牌。密碼一般不允許被他人撤銷。

(3)令牌有權限範圍(scope),比如只能進小區的二號門。對於網絡服務來說,只讀令牌就比讀寫令牌更安全。密碼一般是完整權限。

上面這些設計,保證了令牌既可以讓第三方應用獲得權限,同時又隨時可控,不會危及系統安全。這就是 OAuth 2.0 的優點。

注意,只要知道了令牌,就能進入系統。系統一般不會再次確認身份,所以令牌必須保密,泄漏令牌與泄漏密碼的後果是一樣的。 這也是爲什麼令牌的有效期,一般都設置得很短的原因。

Oauth的授權模式

OAuth 2.0 對於如何頒發令牌的細節,規定得非常詳細。具體來說,針對互聯網的多種場景,一共分成四種授權類型(authorization grant)。

  • 授權碼(authorization-code)
  • 密碼式(password)
  • 隱藏式(implicit)
  • 客戶端憑證(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字段就是令牌。
在這裏插入圖片描述

密碼式(password)
如果你高度信任某個應用,可以把用戶名和密碼直接告訴該應用。該應用就使用你的密碼,申請令牌,這種方式稱爲"密碼式"(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表示"密碼式",usernamepassword是 B 的用戶名和密碼。

第二步,B 網站驗證身份通過後,直接給出令牌。注意,這時不需要跳轉,而是把令牌放在 JSON 數據裏面,作爲 HTTP 迴應,A 因此拿到令牌。

隱藏式(implicit)

有些 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 協議,因此存在"中間人攻擊"的風險,而瀏覽器跳轉時,錨點不會發到服務器,就減少了泄漏令牌的風險。

在這裏插入圖片描述

客戶端憑證(client credentials)

最後一種方式是憑證式(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_idclient_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 網站驗證通過以後,就會頒發新的令牌。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章