常見的伺服器鑑權方式

尹瑞星發表於2021-11-11

Basic Authentication

basic是最簡單的認證機制,客戶端向伺服器端請求資料時,如果未被認證,伺服器會向客戶端傳送驗證請求.

  HTTP/1.0 401 Unauthorised 
  Server: SokEvo/1.0 
  WWW-Authenticate: Basic realm=”google.com” 
  Content-Type: text/html 
  Content-Length: xxx

當客戶端收到401返回值時,將自動彈出一個登陸視窗,要求使用者輸入使用者名稱和密碼.

使用者輸入使用者名稱和密碼後,經過瀏覽器base64編碼後,再次傳送請求

  Get /index.html HTTP/1.0 
  Host:www.google.com 
  Authorization: Basic d2FuZzp3YW5n

伺服器收到請求後將使用者資訊取出解密,對比驗證.

session 是快取在服務端的,cookie 則是快取在客戶端,他們都由服務端生成,為了彌補 Http 協議無狀態的缺陷。

  • 伺服器在接受客戶端首次訪問時在伺服器端建立seesion,然後儲存seesion(我們可以將seesion儲存在記憶體中,也可以儲存在redis中,推薦使用後者),然後給這個session生成一個唯一的標識字串,然後在 響應頭中種下這個唯一標識字串。
  • 簽名。這一步通過祕鑰對sid進行簽名處理,避免客戶端修改sid。(非必需步驟)
  • 瀏覽器中收到請求響應的時候會解析響應頭,然後將sid儲存在本地cookie中,瀏覽器在下次http請求的請求頭中會帶上該域名下的cookie資訊。
  • 伺服器在接受客戶端請求時會去解析請求頭cookie中的sid,然後根據這個sid去找伺服器端儲存的該客戶端的session,然後判斷該請求是否合法。

img

Token

一般 token 由使用者資訊、時間戳和由 hash 演算法加密的簽名構成。

  1. 客戶端使用使用者名稱跟密碼請求登入
  2. 服務端收到請求,去驗證使用者名稱與密碼
  3. 驗證成功後,服務端會簽發一個 Token,再把這個 Token 傳送給客戶端
  4. 客戶端收到 Token 以後可以把它儲存起來,比如放在 Cookie 裡或者Local Storage
  5. 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的 Token
  6. 服務端收到請求,然後去驗證客戶端請求裡面帶著的 Token(request頭部新增Authorization),如果驗證成功,就向客戶端返回請求的資料 ,如果不成功返回401錯誤碼,鑑權失敗。

token認證可以支援多種客戶端,而不僅是瀏覽器,且不受同源策略的影響.

基於token的解決方案有很多,常用的是JWT.JWT 的原理是,伺服器認證以後,生成一個 JSON 物件,這個 JSON 物件肯定不能裸傳給使用者,那誰都可以篡改這個物件傳送請求。因此這個 JSON 物件會被伺服器端簽名加密後返回給使用者,返回的內容就是一張令牌,以後使用者每次訪問伺服器端就帶著這張令牌

JWT由三部分組成: Header.Payload.Signature. Header存放後設資料、Payload存放實際傳遞的資料,因為是Base64編碼的,所以不能存放祕密資訊.Signature是前兩個部分的簽名,防止資料篡改.

特點:

  • JWT 預設是不加密,但也是可以加密的。生成原始 Token 以後,可以用金鑰再加密一次。
  • JWT 不加密的情況下,不能將祕密資料寫入 JWT。
  • JWT 不僅可以用於認證,也可以用於交換資訊。有效使用 JWT,可以降低伺服器查詢資料庫的次數。
  • JWT 的最大缺點是,由於伺服器不儲存 session 狀態,因此無法在使用過程中廢止某個 token,或者更改 token 的許可權。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效,除非伺服器部署額外的邏輯。
  • JWT 本身包含了認證資訊,一旦洩露,任何人都可以獲得該令牌的所有許可權。為了減少盜用,JWT 的有效期應該設定得比較短。對於一些比較重要的許可權,使用時應該再次對使用者進行認證。
  • 為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。

OAuth2.0

OAuth 2.0是目前最流行的授權機制,用來授權第三方應用,獲取使用者資料.

資料所有者告訴系統,同意第三方應用進入系統,獲取這些資料.系統從而產生一個短期的進入令牌用來代替密碼,供第三方應用使用.

令牌是短期有效的,可隨時被資料所有者撤銷,並且有許可權範圍. 以上特點保證了令牌既可以讓第三方應用獲得許可權,同時又隨時可控,不會危機系統安全.

四種授權方式

OAuth引入了一個授權層,用來分離兩種不同的角色: 客戶端和資源所有者.資源所有者同意後,資源伺服器向客戶端頒發令牌,客戶端通過令牌去請求資料.資源伺服器會與對應的授權伺服器通訊、校驗令牌來確定客戶端是否可以訪問資源.

OAuth的核心就是向第三方應用辦法令牌, OAuth規定了四種獲得令牌的流程:

  • 授權碼 (authorization-code): 第三方應用先申請一個授權碼, 然後再用該碼獲取令牌

    這種方式是最常用的流程,安全性也最高,它適用於那些有後端的 Web 應用。授權碼通過前端傳送,令牌則是儲存在後端,而且所有與資源伺服器的通訊都在後端完成。這樣的前後端分離,可以避免令牌洩漏。

    img

    A網站想要得到B網站的令牌,就會提供B網站的帶引數連結,使用者跳轉到B網站,B網站會要求使用者登陸,然後詢問"A 網站要求獲得 xx 許可權,你是否同意?.

    Response_type表示要求返回授權碼

    Client_id 讓B知道是誰在請求(一個應用要求 OAuth 授權,必須先到對方網站登記,讓對方知道是誰在請求。然後該網站會返回client_id和client_secret)

    Scop 表示授權範圍

    https://b.com/oauth/authorize?
      response_type=code&
      client_id=CLIENT_ID&
      redirect_uri=CALLBACK_URL&
      scope=read
    

    同意後,B網站跳回redirect_uri引數指定的網址,並附帶授權碼

    https://a.com/callback?code=AUTHORIZATION_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
    

    B網站收到請求後,向redirect_uri傳送一段json資料, A網站在後端就拿到了.

    {    
      "access_token":"ACCESS_TOKEN",
      "token_type":"bearer",
      "expires_in":2592000,
      "refresh_token":"REFRESH_TOKEN",
      "scope":"read",
      "uid":100101,
      "info":{...}
    }
    
  • 隱藏式 (implicit)

    有些 Web 應用是純前端應用,沒有後端。這時就不能用上面的方式了,必須將令牌儲存在前端。RFC 6749 就規定了第二種方式,允許直接向前端頒發令牌。這種方式沒有授權碼這個中間步驟,所以稱為(授權碼)"隱藏式"(implicit)。

    img

    https://b.com/oauth/authorize?
      response_type=token&
      client_id=CLIENT_ID&
      redirect_uri=CALLBACK_URL&
      scope=read
    

    response_type為token表示直接返回令牌.

    https://a.com/callback#token=ACCESS_TOKEN
    
    Note:
    #代表網頁中的一個位置,當瀏覽器讀取這個URL時,會自動將位置滾動到可視區域.
    為網頁位置指定識別符號,有兩個方法。一是使用錨點,比如<a name="print"></a>,二是使用id屬性,比如<div id="print" >
    #是用來指導瀏覽器動作的,對伺服器端完全無用。所以,HTTP請求中不包括#。
    

    注意,令牌的位置是 URL 錨點(fragment),而不是查詢字串(querystring),這是因為 OAuth 2.0 允許跳轉網址是 HTTP 協議,因此存在"中間人攻擊"的風險,而瀏覽器跳轉時,錨點不會發到伺服器,就減少了洩漏令牌的風險。>

    這種方式把令牌直接傳給前端,是很不安全的。因此,只能用於一些安全要求不高的場景,並且令牌的有效期必須非常短,通常就是會話期間(session)有效,瀏覽器關掉,令牌就失效了。

  • 密碼式 (password)

    如果你高度信任某個應用,RFC 6749 也允許使用者把使用者名稱和密碼,直接告訴該應用。該應用就使用你的密碼,申請令牌,這種方式稱為"密碼式"(password)。

    A網站直接要求使用者提供B網站的使用者名稱和密碼,拿到以後,A就直接向B請求令牌,B網站驗證身份後直接在http回應中給出令牌,不需要跳轉.

    https://oauth.b.com/token?
      grant_type=password&
      username=USERNAME&
      password=PASSWORD&
      client_id=CLIENT_ID
    
  • 客戶端憑證 (client credentials)

    適用於沒有前端的命令列應用,即在命令列下請求令牌。

    https://oauth.b.com/token?
      grant_type=client_credentials&
      client_id=CLIENT_ID&
      client_secret=CLIENT_SECRET
    

使用令牌

A 網站拿到令牌以後,就可以向 B 網站的 API 請求資料了。

此時,每個發到 API 的請求,都必須帶有令牌。具體做法是在請求的頭資訊,加上一個Authorization欄位,令牌就放在這個欄位裡面。

curl -H "Authorization: Bearer ACCESS_TOKEN" \
"https://api.b.com"

更新令牌

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

相關文章