博客 / 詳情

返回

jwt身份認證概述

JWT全稱JSON Web Token

應用流程

  1. 客户端使用用户名和密碼請求登錄,服務端收到請求
  2. 驗證用户名和密碼正確後,後端通過JWT機制,將用户數據作為JWTPayload,同時在前面拼接上一個JWT Header之後進行Base64編碼,並進行簽名,生成一個token,格式為header.payload.signature,返回給客户端
  3. 客户端後續的每次請求都需要攜帶token,攜帶在HTTP Header Authorization
  4. 後端拿到客户端傳遞的token後,進行解密驗證身份,驗證其有效性,檢查簽名是否正確,是否過期,最後解析出JWT Token中的用户信息

優點

  • 無狀態,token機制在服務端不需要存儲session信息,因為token自身包含了所有登錄用户的信息,所以可以減輕服務端壓力
  • 分佈式友好,由於session需要固定保存在一個地方,如果保存在本機,分佈式系統中認證會失效,如果採用redis等統一保存session,系統複雜性會增加
  • 支持跨域訪問,跨域後不會存在信息丟失問題
  • CDN友好,可以通過內容分發網絡請求服務端的所有資料
  • 移動端友好,當客户端是非瀏覽器平台時,cookie是不被支持的,此時採用token認證方式會簡單很多
  • 無需考慮CSRF(Cross Site Request Forgery跨站點請求偽造),token是開發者為了防範csrf而特別設計的令牌,瀏覽器不會自動添加到headers裏,攻擊者也無法訪問用户的token,所以攻擊者提交的表單無法通過服務器認證,也就無法形成攻擊

    CSRF簡述

    1. 在一個瀏覽器中打開了兩個標籤頁,其中一個頁面通過竊取另一個頁面的 cookie 來發送偽造的請求,因為cookie 是隨着瀏覽器請求自動發送到服務端的,這個是CSRF攻擊成功的核心原因
    2. session認證本質需要依賴Cookie,如果cookie被截獲,用户很容易受到跨站請求偽造攻擊
    3. CSRF無法直接竊取到用户的Cookie,header,僅僅是冒用Cookie

缺點

  • 不可控,由於JWT無狀態,想要在JWT 有效期內廢棄一個JWT或者更改它的權限的話,並不會立即生效,通常需要等到有效期過後才可以,如果要避免這個問題,需要把JWT存入redis等緩存,JWT失效的時候就刪除這個JWT,每次驗證的時候查詢一下JWT在不在redis,但是這樣就增加了成本和系統複雜度
  • token續簽不方便,JWT本身payload參數當中攜帶exp參數表示過期時間,payload修改之後簽名也需要修改,所以需要重新生成一個JWT
  • JWT令牌一般會比較長,如果是性能極度敏感的話需要在意這一點

組成

JWT生成的Token由三部分組成:header.payload.signature,通俗地説,JWT的本質是一個字符串,將用户信息保存到一個Json字符串中,然後進行編碼後得到一個JWT token,並且這個JWT token帶有簽名信息,接收後可以校驗是否被篡改,所以可以用於在各方之間安全地將信息作為Json對象傳輸

  • header

    • alg:指定signature採用的加密算法,默認是HS256(HMAC SHA256),對稱加密(加密和解密的密鑰相同)
    • typ:固定值,通常是JWT
    • 通常值是{"alg": "HS256", "typ": "JWT"}, 通過base64Url算法進行編碼之後進行拼接
  • payload

    • 用户idname
    • 默認攜帶iat,令牌簽發時間(時間戳)
    • exp設置令牌過期時間
    • 參數一般形式如下,通過base64Url算法進行編碼與header進行拼接,默認情況下JWT是未加密的,因為只是採用base64算法,拿到JWT字符串後可以轉換回原本的JSON數據,任何人都可以解讀其內容,因此不要構建隱私信息字段,比如用户的密碼一定不能保存到JWT中,JWT只是適合在網絡中傳輸一些非敏感的信息,要傳遞一些敏感數據的話需要使用一些AES或者其他類型的算法,儘量加上一些salt進行加密payload

      {
        "sub": "1234567890",
        "name": "Helen",
        "admin": true
      }
  • signature

    • 設置一個secretKey,通過將前兩個結果合併後進行HS256算法,signature = HS256(base64Url(header)+'.'+base64Url(payload),secretKey)
    • secreKey一定不能暴露,因為可以頒發token,也可以解密

跨域資源共享

是一種基於 HTTP頭的機制,該機制主要是為了避免跨站腳本攻擊而存在

實際網站開發過程當中需要從A網站訪問到另外一個網站B,就需要通過允許服務器標示除了它自己以外的其他源(域、協議或端口),使得瀏覽器允許這些源訪問加載自己的資源,服務器需要返回一個HTTP Header Access-Control-Allow-Origin: * 表明,該資源可以被任意外源訪問,這樣瀏覽器才會正常加載返回的數據

但是當攜帶cookie進行訪問的時候就不能返回一個Header頭表示允許被任意外源訪問

  • 服務器不能Access-Control-Allow-Origin 的值設為通配符“*”,而應將其設置為特定的域,如:Access-Control-Allow-Origin: https://example.com,如果值被設置為*,請求會失敗,如歌設置為具體的域請求成功
  • 服務器不能Access-Control-Allow-Headers 的值設為通配符“*”,而應將其設置為標頭名稱的列表,如:Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
  • 服務器不能Access-Control-Allow-Methods 的值設為通配符“*”,而應將其設置為特定請求方法名稱的列表,如:Access-Control-Allow-Methods: POST, GET

詳情參考Mozilla對跨域資源共享的定義

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

python實現

依賴項目pyjwt

https://github.com/jpadilla/pyjwt

安裝

$ pip install pyjwt

使用

>>> import jwt
>>> encoded = jwt.encode({"some": "payload"}, "secret", algorithm="HS256")
>>> print(encoded)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg
>>> jwt.decode(encoded, "secret", algorithms=["HS256"])
{'some': 'payload'}

參考閲讀

掘金-JWTToken詳解

掘金-JWT詳解

Mozilla 跨源資源共享CORS

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.