認證授權:學習OAuth協議

cwsheng發表於2020-08-23

1、什麼是OAuth協議?

  OAUTH協議為使用者資源的授權提供了一個安全的、開放而又簡易的標準。同時,任何第三方都可以使用OAuth認證服務,任何服務提供商都可以實現自身的OAuth認證服務,因而OAuth是開放的。業界提供了OAuth的多種實現如PHP、JavaScript,Java,Ruby等各種語言開發包,大大節約了程式設計師的時間,因而OAuth是簡易的。網際網路很多服務如Open API,很多大公司如Google,Yahoo,Microsoft等都提供了OAuth認證服務,這些都足以說明OAuth標準逐漸成為開放資源授權的標準。

  目前最新版本為OAuth2.0,主要參考材料為RFC 6749

  本文主要介紹OAuth2.0

2、思路和特點

  思路:

   OAuth本質是在"客戶端"與"服務提供商"之間,設定了一個授權層(authorization layer)。"客戶端"不能直接登入"服務提供商",只能登入授權層,以此將使用者與客戶端區分開來。"客戶端"登入授權層所用的令牌(token),與使用者的密碼不同。使用者可以在登入的時候,指定授權層令牌的許可權範圍和有效期。

  特點:  

  (1). 簡單:不管是OAuth服務提供者還是應用開發者,都很易於理解與使用;

(2). 安全:沒有涉及到使用者金鑰等資訊,更安全更靈活;

(3). 開放:任何服務提供商都可以實現OAuth,任何軟體開發商都可以使用OAuth;

3、名詞定義

(1)Third-party application:第三方應用程式,下圖中又稱"客戶端"(Client:上一篇文中簡書

(2)Resource Owner:資源所有者,通常為"使用者"(User:上一篇中的QQ使用者)

(3)Authorization Server:認證伺服器,即服務提供商專門用來處理認證的伺服器。(上一篇文中的QQ互聯服務)

(4)Resource Server:資源伺服器,即服務提供商存放使用者生成的資源的伺服器。它與認證伺服器,可以是同一臺伺服器,也可以是不同的伺服器。(上一篇文中的QQ資訊服務)

(5)User Agent:使用者代理,通常指瀏覽器。

(6)HTTP service:HTTP服務提供商

(7)Access Token:訪問令牌,授權後得到的憑證

(8)Refresh Token:重新整理令牌是用於獲取訪問令牌的憑據。

 4、執行流程

  OAuth 2.0的執行流程如下圖,摘自RFC 6749。

  

步驟詳解:(對應上圖步驟)

(A)使用者開啟客戶端以後,客戶端要求使用者給予授權。 (B)使用者同意給予客戶端授權。 (C)客戶端使用上一步獲得的授權,向認證伺服器申請令牌。 (D)認證伺服器對客戶端進行認證以後,確認無誤,同意發放令牌。 (E)客戶端使用令牌,向資源伺服器申請獲取資源。 (F)資源伺服器確認令牌無誤,同意向客戶端開放受保護的資源。

  可以看出,上面步驟中,(B)是關鍵,即使用者怎樣給客戶端授權。有了這個授權以後,客戶端就可以獲取令牌,進而憑令牌獲取資源。

  下面我們來講解OAuth提供的4種授權方式。

5、客戶端授權模式:

   客戶端必須得到使用者的授權(authorization grant),才能獲得令牌(access token)最後才能憑令牌獲取受保護的資源

   OAuth 2.0定義了四種授權方式和重新整理Token

    • 授權碼模式(Authorization Code)
    • 隱式模式(Implicit Grant )
    • 密碼模式(Resource Owner Password Credentials)
    • 客戶端模式(Client Credentials)
    • 重新整理令牌(Refresh Token

  5.1 授權碼模式(Authorization Code):

    授權碼模式(authorization code)是功能最完整、流程最嚴密的授權模式。它的特點就是通過客戶端的後臺伺服器,與"服務提供商"的認證伺服器進行互動。

    

步驟如下:

(A)使用者訪問客戶端,後者將前者導向認證伺服器。

  例子:https://graph.qq.com/oauth2.0/show?which=Login&display=pc&client_id=100410602&redirect_uri=http%3A%2F%2Fwww.jianshu.com%2Fusers%2Fauth%2Fqq_connect%2Fcallback&response_type=code&state=%257B%257D

  引數:

    • response_type:表示授權型別,必選項,此處的值固定為"code"
    • client_id:表示客戶端的ID,必選項
    • redirect_uri:表示重定向URI,可選項
    • scope:表示申請的許可權範圍,可選項
    • state:表示客戶端的當前狀態,可以指定任意值,認證伺服器會原封不動地返回這個值。這用於防止CSRF攻擊

(B)使用者選擇是否給予客戶端授權。  

(C)假設使用者給予授權,認證伺服器將使用者導向客戶端事先指定的"重定向URI"(redirection URI),同時附上一個授權碼。

  例子:http://www.jianshu.com/users/auth/qq_connect/callback?code=B36BD11F2DF8ED948F8CDE81CDDD0EEB&state=%257B%257D

  引數:    

    • code:表示授權碼,必選項。該碼的有效期應該很短,通常設為10分鐘,客戶端只能使用該碼一次,否則會被授權伺服器拒絕。該碼與客戶端ID和重定向URI,是一一對應關係。
    • state:表示客戶端的當前狀態,可以指定任意值,認證伺服器會原封不動地返回這個值。這用於防止CSRF攻擊

(D)客戶端收到授權碼,附上早先的"重定向URI",向認證伺服器申請令牌。這一步是在客戶端的後臺的伺服器上完成的,對使用者不可見。

  例子:https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=[YOUR_APP_ID]&client_secret=[YOUR_APP_Key]&code=[The_AUTHORIZATION_CODE]&redirect_uri=[YOUR_REDIRECT_URI]

  引數:  

    • grant_type:表示使用的授權模式,必選項,此處的值固定為"authorization_code"。
    • code:表示上一步獲得的授權碼,必選項。
    • redirect_uri:表示重定向URI,必選項,且必須與A步驟中的該引數值保持一致。
    • client_id:表示客戶端ID,必選項。

(E)認證伺服器核對了授權碼和重定向URI,確認無誤後,向客戶端傳送訪問令牌(access token)和更新令牌(refresh token)。

  例子:    

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache    

{ "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3", "token_type":"bearer", "expires_in":3600, "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk", "scope":"create delete"}

引數使用JSON格式傳送(Content-Type: application/json)。此外,HTTP頭資訊中明確指定不得快取

  說明:

    • access_token:表示訪問令牌,必選項。
    • token_type:表示令牌型別,該值大小寫不敏感,必選項,可以是bearer型別或mac型別。
    • expires_in:表示過期時間,單位為秒。如果省略該引數,必須其他方式設定過期時間。
    • refresh_token:表示更新令牌,用來獲取下一次的訪問令牌,可選項。
    • scope:表示許可權範圍,如果與客戶端申請的範圍一致,此項可省略。

 

  5.2 簡化模式(Implicit Grant ):

    簡化模式(implicit grant type)不通過第三方應用程式的伺服器,直接在瀏覽器中向認證伺服器申請令牌,跳過了"授權碼"這個步驟。所有步驟在瀏覽器中完成,令牌對訪問者是可見的,且客戶端不需要認證。

    

步驟如下:

(A)客戶端將使用者導向認證伺服器。

  例子:

    https://graph.qq.com/oauth2.0/authorize?response_type=token&client_id=[YOUR_APPID]&redirect_uri=[YOUR_REDIRECT_URI]&scope=[THE_SCOPE]

   引數:

    • response_type:表示授權型別,此處的值固定為"token",必選項。
    • client_id:表示客戶端的ID,必選項。
    • redirect_uri:表示重定向的URI,可選項。
    • scope:表示許可權範圍,可選項。
    • state:表示客戶端的當前狀態,可以指定任意值,認證伺服器會原封不動地返回這個值。

(B)使用者決定是否給於客戶端授權。 

(C)假設使用者給予授權,認證伺服器將使用者導向客戶端指定的"重定向URI",並在URI的Hash部分包含了訪問令牌。

   引數:

    • access_token:表示訪問令牌,必選項。
    • token_type:表示令牌型別,該值大小寫不敏感,必選項。
    • expires_in:表示過期時間,單位為秒。如果省略該引數,必須其他方式設定過期時間。
    • scope:表示許可權範圍,如果與客戶端申請的範圍一致,此項可省略。
    • state:如果客戶端的請求中包含這個引數,認證伺服器的回應也必須一模一樣包含這個引數。

(D)瀏覽器向資源伺服器發出請求,其中不包括上一步收到的Hash值。 

(E)資源伺服器返回一個網頁,其中包含的程式碼可以獲取Hash值中的令牌。 

(F)瀏覽器執行上一步獲得的指令碼,提取出令牌。 

(G)瀏覽器將令牌發給客戶端。 

 

  5.3 密碼模式(Resource Owner Password Credentials)

    密碼模式(Resource Owner Password Credentials Grant)中,使用者向客戶端提供自己的使用者名稱和密碼。客戶端使用這些資訊,向"服務商提供商"索要授權。

    在這種模式中,使用者必須把自己的密碼給客戶端,但是客戶端不得儲存密碼。這通常用在使用者對客戶端高度信任的情況下

    


步驟如下:
(A)使用者向客戶端提供使用者名稱和密碼。 (B)客戶端將使用者名稱和密碼發給認證伺服器,向後者請求令牌。
  例子:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w

  引數:    
    • grant_type:表示授權型別,此處的值固定為"password",必選項。
    • username:表示使用者名稱,必選項。
    • password:表示使用者的密碼,必選項。
    • scope:表示許可權範圍,可選項。
(C)認證伺服器確認無誤後,向客戶端提供訪問令牌。
  結果:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
"access_token":"XXX",
"token_type":"example",
"expires_in":3600,
"refresh_token":"XXX",
"example_parameter":"XXX"
}

 

  5.4 客戶端模式(Client Credentials)

    客戶端模式(Client Credentials Grant)指客戶端以自己的名義,而不是以使用者的名義,向"服務提供商"進行認證。嚴格地說,客戶端模式並不屬於OAuth框架所要解決的問題。在這種模式中,使用者直接向客戶端註冊,客戶端以自己的名義要求"服務提供商"提供服務,其實不存在授權問題。

     


步驟如下:
(A)客戶端向認證伺服器進行身份認證,並要求一個訪問令牌。
   引數: 
    • granttype:表示授權型別,此處的值固定為"clientcredentials",必選項。
    • scope:表示許可權範圍,可選項。
(B)認證伺服器確認無誤後,向客戶端提供訪問令牌。
   結果:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}

  5.5 重新整理令牌(Refresh Token)

     

步驟:
(A)客戶端通過與授權伺服器進行身份驗證並提供授權授權來請求訪問令牌。
(B)授權伺服器對客戶端進行身份驗證並驗證授權,如果有效,則發出訪問令牌和重新整理令牌。
(C)客戶端通過提供訪問令牌向資源伺服器發出受保護的資源請求
(D)資源伺服器驗證訪問令牌,如果有效,則為請求提供服務結果
(E)重複步驟(C)和(D),直到訪問令牌過期。如果客戶機知道訪問令牌已過期,它將跳到步驟(G);否則,它將發出另一個受保護資源請求。
(F)由於訪問令牌無效,資源伺服器返回無效令牌錯誤。
(G)客戶端通過向授權伺服器進行身份驗證並顯示重新整理令牌來請求新的訪問令牌。客戶端身份驗證要求基於客戶端型別和授權伺服器策略。
  例子:
    POST /token HTTP/1.1
    Host: server.example.com
    Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
  引數: 
    • granttype:表示使用的授權模式,此處的值固定為"refreshtoken",必選項。
    • refresh_token:表示早前收到的更新令牌,必選項。
    • scope:表示申請的授權範圍,不可以超出上一次申請的範圍,如果省略該引數,則表示與上一次一致。
(H)授權伺服器對客戶端進行身份驗證並驗證重新整理令牌,如果有效,則發出新的訪問令牌(以及新重新整理令牌)。

6、JSON Web Token (JWT)

  JWT是一個定義一種緊湊的自包含的並且提供防篡改機制的傳遞資料的方式的標準協議。

  如下示例:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imxpbmlhbmh1aSJ9.hnOfZb95jFwQsYj3qlgFbUu1rKpfTE6AzgXZidEGGTk

  上面示例看著複雜,其實 JWT由3部分構成:header.payload.signature,每個部分由“.”來分割開來。

 6.1 Header

  header是一個有效的JSON,其中通常包含了兩部分:token型別和簽名演算法。

{
  "alg": "HS256",
  "typ": "JWT"
}

  對這個JSON採用base64編碼後就是第1部分eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

 6.2 Payload

  這一部分代表真正想要傳遞的資料,包含一組Claims,其中JWT預定義了一些Claim後面會介紹。

{
  "sub": "1234567890",
  "name": "cba"
}

  對JSON採用base64編碼後就是第2部分eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imxpbmlhbmh1aSJ9

 6.3 Signature

  這一部分是可選的,由於前面Header和Payload部分是明文的資訊,所以這一部分的意義在於保障資訊不被篡改用的,生成這部分的方式如下:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

  token生成方使用header中指定的簽名演算法對“header.payload”部分進行簽名,得到的第3部分hnOfZb95jFwQsYj3qlgFbUu1rKpfTE6AzgXZidEGGTk,然後組合成一個完整的JWT字串 . 而token消費方在拿到token後, 使用同樣的簽名演算法來生成簽名,用來判斷header和payload部分有沒有被篡改過,因為簽名的金鑰是隻有通訊雙方知道的,所以可以保證這部分資訊不被第三方所篡改。

 6.4 JWT的一些Claims

JWT規範中預先定義了一些Cliam,但並不是必選的,常用的有:

  • iss(Issuer簽發者)
  • sub(subject簽發給的受眾,在Issuer範圍內是唯一的)
  • aud(Audience接收方)
  • exp(Expiration Time過期時間)
  • iat(Issued At簽發時間)等等。

後續:

  後續將繼續介紹認證授權-中ODIC的內容

引用:

 

相關文章