JWT全稱JSON Web Token
應用流程
- 客户端使用用户名和密碼請求登錄,服務端收到請求
- 驗證用户名和密碼正確後,後端通過
JWT機制,將用户數據作為JWT的Payload,同時在前面拼接上一個JWT Header之後進行Base64編碼,並進行簽名,生成一個token,格式為header.payload.signature,返回給客户端 - 客户端後續的每次請求都需要攜帶
token,攜帶在HTTP Header Authorization中 - 後端拿到客户端傳遞的
token後,進行解密驗證身份,驗證其有效性,檢查簽名是否正確,是否過期,最後解析出JWT Token中的用户信息
優點
- 無狀態,
token機制在服務端不需要存儲session信息,因為token自身包含了所有登錄用户的信息,所以可以減輕服務端壓力 - 分佈式友好,由於
session需要固定保存在一個地方,如果保存在本機,分佈式系統中認證會失效,如果採用redis等統一保存session,系統複雜性會增加 - 支持跨域訪問,跨域後不會存在信息丟失問題
CDN友好,可以通過內容分發網絡請求服務端的所有資料- 移動端友好,當客户端是非瀏覽器平台時,
cookie是不被支持的,此時採用token認證方式會簡單很多 -
無需考慮
CSRF(Cross Site Request Forgery跨站點請求偽造),token是開發者為了防範csrf而特別設計的令牌,瀏覽器不會自動添加到headers裏,攻擊者也無法訪問用户的token,所以攻擊者提交的表單無法通過服務器認證,也就無法形成攻擊CSRF簡述- 在一個瀏覽器中打開了兩個標籤頁,其中一個頁面通過竊取另一個頁面的
cookie來發送偽造的請求,因為cookie是隨着瀏覽器請求自動發送到服務端的,這個是CSRF攻擊成功的核心原因 session認證本質需要依賴Cookie,如果cookie被截獲,用户很容易受到跨站請求偽造攻擊CSRF無法直接竊取到用户的Cookie,header,僅僅是冒用Cookie
- 在一個瀏覽器中打開了兩個標籤頁,其中一個頁面通過竊取另一個頁面的
缺點
- 不可控,由於
JWT無狀態,想要在JWT有效期內廢棄一個JWT或者更改它的權限的話,並不會立即生效,通常需要等到有效期過後才可以,如果要避免這個問題,需要把JWT存入redis等緩存,JWT失效的時候就刪除這個JWT,每次驗證的時候查詢一下JWT在不在redis,但是這樣就增加了成本和系統複雜度 token續簽不方便,JWT本身payload參數當中攜帶exp參數表示過期時間,payload修改之後簽名也需要修改,所以需要重新生成一個JWTJWT令牌一般會比較長,如果是性能極度敏感的話需要在意這一點
組成
JWT生成的Token由三部分組成:header.payload.signature,通俗地説,JWT的本質是一個字符串,將用户信息保存到一個Json字符串中,然後進行編碼後得到一個JWT token,並且這個JWT token帶有簽名信息,接收後可以校驗是否被篡改,所以可以用於在各方之間安全地將信息作為Json對象傳輸
-
headeralg:指定signature採用的加密算法,默認是HS256(HMAC SHA256),對稱加密(加密和解密的密鑰相同)typ:固定值,通常是JWT- 通常值是
{"alg": "HS256", "typ": "JWT"}, 通過base64Url算法進行編碼之後進行拼接
-
payload- 用户
id和name - 默認攜帶
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'}
參考閲讀
掘金-JWT與Token詳解
掘金-JWT詳解
Mozilla 跨源資源共享CORS