Django REST framework JWT
- 一、JWT簡介
- 二、JWT 組成
- header
- payload
- signature
- 三.使用
- 手動生成jwt
- 前端保存jwt
一、JWT簡介
JWT(Json Web Token) 是一個開放標準(RFC 7519),它定義了一種用于簡潔,自包含的用于通信雙方之間以 JSON 物件的形式安全傳遞資訊的方法,JWT 可以使用 HMAC 演算法或者是 RSA 的公鑰密鑰對進行簽名,它具備兩個特點:
簡潔(Compact)
可以通過URL, POST 引數或者在 HTTP header 發送,因為資料量小,傳輸速度快
自包含(Self-contained)
負載中包含了所有用戶所需要的資訊,避免了多次查詢資料庫
二、JWT 組成
JWT就一段字串,由三段資訊構成的,將這三段資訊文本用.鏈接一起就構成了Jwt字串,就像這樣:

第一部分我們稱它為頭部(header),第二部分我們稱其為載荷(payload, 類似于飛機上承載的物品),第三部分是簽證(signature).
header
jwt的頭部承載兩部分資訊:
- 宣告型別,這里是jwt
- 宣告加密的演算法 通常直接使用 HMAC SHA256
完整的頭部就像下面這樣的JSON:
{
'typ': 'JWT',
'alg': 'HS256'
}
然后將頭部進行base64.b64encode()加密(該加密是可以對稱解密的),構成了第一部分.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
python中base64加密解密
import base64
str1 = 'admin'
str2 = str1.encode()
b1 = base64.b64encode(str2) #資料越多,加密后的字串越長
b2 = base64.b64decode(b1) #admin
各個語言中都有base64加密解密的功能,所以我們jwt為了安全,需要配合第三段加密
payload
載荷就是存放有效資訊的地方,這個名字像是特指飛機上承載的貨品,這些有效資訊可以存放下面三個部分資訊,
- 標準中注冊的宣告
- 公共的宣告
- 私有的宣告
標準中注冊的宣告 (建議但不強制使用) :
-
iss: jwt簽發者
-
sub: jwt所面向的用戶
-
aud: 接收jwt的一方
-
exp: jwt的過期時間,這個過期時間必須要大于簽發時間
-
nbf: 定義在什么時間之前,該jwt都是不可用的.
-
iat: jwt的簽發時間
-
jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊,
以上是JWT 規定的7個官方欄位,供選用
公共的宣告 : 公共的宣告可以添加任何的資訊,一般添加用戶的相關資訊或其他業務需要的必要資訊.但不建議添加敏感資訊,因為該部分在客戶端可解密.
私有的宣告 : 私有宣告是提供者和消費者所共同定義的宣告,一般不建議存放敏感資訊,因為base64是對稱解密的,意味著該部分資訊可以歸類為明文資訊,
定義一個payload,json格式的資料:
{
"sub": "1234567890",
"exp": "3422335555", #時間戳形式
"name": "John Doe",
"admin": true
}
然后將其進行base64.b64encode() 加密,得到JWT的第二部分,
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature
JWT的第三部分是一個簽證資訊,這個簽證資訊由三部分組成:
- header (base64后的)
- payload (base64后的)
- secret密鑰
這個部分需要base64加密后的header和base64加密后的payload使用.連接組成的字串,然后通過header中宣告的加密方式進行加鹽secret組合加密,然后就構成了jwt的第三部分,
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); //xxxx // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
將這三部分用.連接成一個完整的字串,構成了最終的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意:secret是保存在服務器端的,jwt的簽發生成也是在服務器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去,一旦客戶端得知這個secret, 那就意味著客戶端是可以自我簽發jwt了,
jwt的優點:
1. 實作分布式的單點登陸非常方便
2. 資料實際保存在客戶端,所以我們可以分擔都武器的存盤壓力
3. JWT不僅可用于認證,還可用于資訊交換,善用JWT有助于減少服務器請求資料庫的次數,jwt的構成非常簡單,位元組占用很小,所以它是非常便于傳輸的,
jwt的缺點:
1. 資料保存在了客戶端,我們服務端只認jwt,不識別客戶端,
2. jwt可以設定過期時間,但是因為資料保存在了客戶端,所以對于過期時間不好調整,#secret_key輕易不要改,一改所有客戶端都要重新登錄
認證流程圖:

首先,前端通過Web表單將自己的用戶名和密碼發送到后端的介面,這一程序一般是一個HTTP POST請求,建議的方式是通過SSL加密的傳輸(https協議),從而避免敏感資訊被嗅探,
后端核對用戶名和密碼成功后,將用戶的id等其他資訊作為JWT Payload(負載),將其與頭部分別進行Base64編碼拼接后簽名,形成一個JWT,形成的JWT就是一個形同lll.zzz.xxx的字串,
后端將JWT字串作為登錄成功的回傳結果回傳給前端,前端可以將回傳的結果保存在localStorage或sessionStorage上,退出登錄時前端洗掉保存的JWT即可,
前端在每次請求時將JWT放入HTTP Header中的Authorization位,(解決XSS和XSRF問題)
5.后端檢查是否存在,如存在驗證JWT的有效性,例如,檢查簽名是否正確;檢查Token是否過期;檢查Token的接收方是否是自己(可選),
三.使用
1.安裝:
pip install djangorestframework-jwt -i https://mirrors.aliyun.com/pypi/simple/
2.配置
配置https://github.com/jpadilla/django-rest-framework-jwt
在settings檔案中:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
import datetime
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
}
- JWT_EXPIRATION_DELTA 指明token的有效期
手動生成jwt
Django REST framework JWT 擴展的說明檔案中提供了手動簽發JWT的方法
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
在子應用路由urls.py中
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path(r'login/', obtain_jwt_token),
]
在主路由中,引入當前子應用的路由檔案
urlpatterns = [
...
path('user/', include("users.urls")),
# include 的值必須是 模塊名.urls 格式,字串中間只能出現一個圓點
]
前端保存jwt
我們可以將JWT保存在cookie中,也可以保存在瀏覽器的本地存盤里,我們保存在瀏覽器本地存盤中
瀏覽器的本地存盤提供了sessionStorage 和 localStorage 兩種,從屬于window物件:
- sessionStorage 瀏覽器關閉即失效
- localStorage 長期有效
使用方法
sessionStorage.變數名 = 變數值 // 保存資料
sessionStorage.setItem("變數名","變數值") // 保存資料
sessionStorage.變數名 // 讀取資料
sessionStorage.getItem("變數名") // 讀取資料
sessionStorage.removeItem("變數名") // 清除單個資料
sessionStorage.clear() // 清除所有sessionStorage保存的資料
localStorage.變數名 = 變數值 // 保存資料
localStorage.setItem("變數名","變數值") // 保存資料
localStorage.變數名 // 讀取資料
localStorage.getItem("變數名") // 讀取資料
localStorage.removeItem("變數名") // 清除單個資料
localStorage.clear() // 清除所有sessionStorage保存的資料
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/200654.html
標籤:其他
