Django REST framework JWT

来源:https://www.cnblogs.com/QiaoPengjun/archive/2022/04/10/16127893.html
-Advertisement-
Play Games

Django REST framework JWT 在用戶註冊或登錄後,我們想記錄用戶的登錄狀態,或者為用戶創建身份認證的憑證。我們不再使用Session認證機制,而使用Json Web Token認證機制。 Json web token (JWT), 是為了在網路應用環境間傳遞聲明而執行的一種基於 ...


Django REST framework JWT

在用戶註冊或登錄後,我們想記錄用戶的登錄狀態,或者為用戶創建身份認證的憑證。我們不再使用Session認證機制,而使用Json Web Token認證機制。

Json web token (JWT), 是為了在網路應用環境間傳遞聲明而執行的一種基於JSON的開放標準((RFC 7519).該token被設計為緊湊且安全的,特別適用於分散式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者(客戶端)和服務提供者(服務端)間傳遞被認證的用戶身份信息,以便於從資源伺服器獲取資源,也可以增加一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用於身份認證,也可被數據加密傳輸。

JWT的構成

JWT就一段字元串,由三段信息構成的,將這三段信息文本用.拼接一起就構成了Jwt token字元串。就像這樣:

eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAiMTUwMTIzNDU1IiwgImlhdCI6ICIxNTAxMDM0NTUiLCAibmFtZSI6ICJ3YW5neGlhb21pbmciLCAiYWRtaW4iOiB0cnVlLCAiYWNjX3B3ZCI6ICJRaUxDSmhiR2NpT2lKSVV6STFOaUo5UWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjkifQ==.815ce0e4e15fff813c5c9b66cfc3791c35745349f68530bc862f7f63c9553f4b

第一部分我們稱它為頭部(header),第二部分我們稱其為載荷(payload, 類似於飛機上承載的物品),第三部分是簽證(signature).

header

jwt的頭部承載兩部分信息:

  • typ: 聲明token類型,這裡是jwt ,typ的值也可以是:Bear
  • alg: 聲明簽證的加密的演算法 通常直接使用 HMAC SHA256

完整的頭部就像下麵這樣的JSON:

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

然後將頭部進行base64編碼,構成了jwt的第一部分頭部

python代碼舉例:

import base64, json
header_data = {"typ": "jwt", "alg": "HS256"}
header = base64.b64encode( json.dumps(header_data).encode() ).decode()
print(header) # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9

payload

載荷就是存放有效信息的地方。這個名字像是特指飛機上承載的貨倉,這些有效信息包含三個部分:

  • 標準聲明
  • 公共聲明
  • 私有聲明

標準聲明指定jwt實現規範中要求的屬性。 (建議但不強制使用) :

  • iss: jwt簽發者
  • sub: jwt所面向的用戶
  • aud: 接收jwt的一方
  • exp: jwt的過期時間,這個過期時間必須要大於簽發時間
  • nbf: 定義在什麼時間之前,該jwt都是不可用的.
  • iat: jwt的簽發時間
  • jti: jwt的唯一身份標識,主要用來作為一次性token, 從而迴避重放攻擊。

公共聲明 : 公共的聲明可以添加任何的公開信息,一般添加用戶的相關信息或其他業務需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密.

私有聲明 : 私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,裡面存放的是一些可以在服務端或者客戶端通過秘鑰進行加密和解密的加密信息。往往採用的RSA非對稱加密演算法。

python舉例,定義一個payload。

import base64, json
from datetime import datetime
iat = int(datetime.now().timestamp())
payload_data = {
    "sub": "root",
    "exp": iat+3600,  # 假設一小時過期
    "iat": iat,
    "name": "wangxiaoming",
    "admin": True,
    "acc_pwd": "QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9",
}
# 將其進行base64編碼,得到JWT的第二部分。
payload = base64.b64encode(json.dumps(payload_data).encode()).decode()
print(payload) # eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTI4NDQ4LCAiaWF0IjogMTYzMTUyNDg0OCwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=

signature

JWT的第三部分是一個簽證信息,用於辨真偽,防篡改。這個簽證信息由三部分組成:

  • header (base64後的頭部)

  • payload (base64後的載荷)

  • secret(保存在服務端的秘鑰字元串,不會提供給客戶端的)

    import base64, json, hashlib

    if name == 'main':
    """jwt 頭部的生成"""
    header_data = {"typ": "jwt", "alg": "HS256"}
    header = base64.b64encode( json.dumps(header_data).encode() ).decode()
    print(header) # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9

      """jwt 載荷的生成"""
      payload_data = {
          "sub": "root",
          "exp": "150123455",
          "iat": "150103455",
          "name": "wangxiaoming",
          "admin": True,
          "acc_pwd": "QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9",
      }
      # 將其進行base64編碼,得到JWT的第二部分。
      payload = base64.b64encode(json.dumps(payload_data).encode()).decode()
      print(payload) # eyJzdWIiOiAicm9vdCIsICJleHAiOiAiMTUwMTIzNDU1IiwgImlhdCI6ICIxNTAxMDM0NTUiLCAibmFtZSI6ICJ3YW5neGlhb21pbmciLCAiYWRtaW4iOiB0cnVlLCAiYWNjX3B3ZCI6ICJRaUxDSmhiR2NpT2lKSVV6STFOaUo5UWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjkifQ==
    
      # from django.conf import settings
      # secret = settings.SECRET_KEY
      secret = 'django-insecure-hbcv-y9ux0&8qhtkgmh1skvw#v7ru%t(z-#chw#9g5x1r3z=$p'
      data = header + payload + secret  # 秘鑰絕對不能提供給客戶端。
      HS256 = hashlib.sha256()
      HS256.update(data.encode('utf-8'))
      signature = HS256.hexdigest()
      print(signature) # 815ce0e4e15fff813c5c9b66cfc3791c35745349f68530bc862f7f63c9553f4b
    
      # jwt 最終的生成
      token = f"{header}.{payload}.{signature}"
      print(token)
      # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAiMTUwMTIzNDU1IiwgImlhdCI6ICIxNTAxMDM0NTUiLCAibmFtZSI6ICJ3YW5neGlhb21pbmciLCAiYWRtaW4iOiB0cnVlLCAiYWNjX3B3ZCI6ICJRaUxDSmhiR2NpT2lKSVV6STFOaUo5UWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjkifQ==.815ce0e4e15fff813c5c9b66cfc3791c35745349f68530bc862f7f63c9553f4b
    

註意:secret是保存在伺服器端的,jwt的簽發生成也是在伺服器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味著客戶端是可以自我簽發jwt了。

python代碼舉例:

import base64, json, hashlib
from datetime import datetime

if __name__ == '__main__':
    """jwt 頭部的生成"""
    header_data = {"typ": "jwt", "alg": "HS256"}
    header = base64.b64encode( json.dumps(header_data).encode() ).decode()
    print(header) # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9

    """jwt 載荷的生成"""
    iat = int(datetime.now().timestamp())
    payload_data = {
        "sub": "root",
        "exp": iat+3600,  # 假設1小時過期
        "iat": iat,
        "name": "wangxiaoming",
        "admin": True,
        "acc_pwd": "QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9QiLCJhbGciOiJIUzI1NiJ9",
    }
    # 將其進行base64編碼,得到JWT的第二部分。
    payload = base64.b64encode(json.dumps(payload_data).encode()).decode()
    print(payload) # eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTI4NDQ4LCAiaWF0IjogMTYzMTUyNDg0OCwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=

    # from django.conf import settings
    # secret = settings.SECRET_KEY
    SECRET_KEY = 'django-insecure-hbcv-y9ux0&8qhtkgmh1skvw#v7ru%t(z-#chw#9g5x1r3z=$p'
    data = header + payload + SECRET_KEY  # 秘鑰絕對不能提供給客戶端。
    HS256 = hashlib.sha256()
    HS256.update(data.encode('utf-8'))
    signature = HS256.hexdigest()
    print(signature) # 815ce0e4e15fff813c5c9b66cfc3791c35745349f68530bc862f7f63c9553f4b

    # jwt 最終的生成
    token = f"{header}.{payload}.{signature}"
    print(token)
    # eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTI4NTA2LCAiaWF0IjogMTYzMTUyNDkwNiwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=.a8c677945fc277d8e677514420a1bff645da00498c2af8fb29189d652391a435

    """驗證邏輯"""
    # token = "eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTI4NTA2LCAiaWF0IjogMTYzMTUyNDkwNiwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=.a8c677945fc277d8e677514420a1bff645da00498c2af8fb29189d652391a435"
    # token = "eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAicm9vdCIsICJleHAiOiAxNjMxNTIxNzA5LCAiaWF0IjogMTYzMTUxODEwOSwgIm5hbWUiOiAid2FuZ3hpYW9taW5nIiwgImFkbWluIjogdHJ1ZSwgImFjY19wd2QiOiAiUWlMQ0poYkdjaU9pSklVekkxTmlKOVFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5In0=.b82524a2155a24bcb4f085993545ec74766688849fd832e4edf9dd592167a2e3"
    token = "eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiJyb290IiwiZXhwIjoxNjMxNTI5MDg4LCJpYXQiOjE2MzE1MjU0ODgsIm5hbWUiOiJ3YW5neGlhb2hvbmciLCJhZG1pbiI6dHJ1ZSwiYWNjX3B3ZCI6IlFpTENKaGJHY2lPaUpJVXpJMU5pSjlRaUxDSmhiR2NpT2lKSVV6STFOaUo5UWlMQ0poYkdjaU9pSklVekkxTmlKOSJ9.b533c5515444c51058557017e433d411379862d91640c8beed6f2617b1da2feb"
    header, payload, signature = token.split(".")

    # 驗證是否過期了
    payload_data = json.loads( base64.b64decode(payload.encode()) )
    exp = payload_data.get("exp",None)
    if exp is not None and int(exp) < int(datetime.now().timestamp()):
        raise Exception("token過期!!!")

    # 驗證token是否有效,是否被串改
    # from django.conf import settings
    # secret = settings.SECRET_KEY
    SECRET_KEY = 'django-insecure-hbcv-y9ux0&8qhtkgmh1skvw#v7ru%t(z-#chw#9g5x1r3z=$p' # 獲取原來的秘鑰
    data = header + payload + SECRET_KEY  # 秘鑰絕對不能提供給客戶端。
    HS256 = hashlib.sha256()
    HS256.update(data.encode('utf-8'))
    new_signature = HS256.hexdigest()

    if new_signature != signature:
        print("認證失敗")
    else:
        print("認證通過")

關於簽發和核驗JWT,我們可以使用Django REST framework JWT擴展來完成。

文檔網站:https://jpadilla.github.io/django-rest-framework-jwt/

安裝配置JWT

安裝

pip install djangorestframework-jwt

settings.dev,配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}
import datetime
JWT_AUTH = {
    # 設置jwt的有效期
    'JWT_EXPIRATION_DELTA': datetime.timedelta(weeks=1),  # 一周有效
}
  • JWT_EXPIRATION_DELTA 指明token的有效期

生成jwt

Django REST framework JWT 擴展的說明文檔中提供了手動簽發JWT的方法

https://jpadilla.github.io/django-rest-framework-jwt/#creating-a-new-token-manually

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)

在用戶註冊或登錄成功後,在序列化器中返回用戶信息以後同時返回token即可。

後端實現登陸認證介面

Django REST framework JWT提供了登錄獲取token的視圖,可以直接使用它綁定一個url地址即可。

在users子應用路由urls.py中

from rest_framework_jwt.views import obtain_jwt_token
from django.urls import path
urlpatterns = [
    path('login/', obtain_jwt_token),
]

接下來,我們可以通過postman來測試下功能,可以發送form表單,也可以發送json,username和password是必填欄位


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 正文 1. 阿裡雲DataV 2. 積木報表jimureport 3. 百度Sugar 4. 帆軟 最經常的工作是將一些項目的數據從資料庫導出,然後分門別類的列到excel表格中,領導看起來眼花繚亂。 要是能以圖表可視化展現出來,領導就可以看到項目近幾個月的走勢,也知道之後要怎麼決策了。 嘗試了使用 ...
  • 前端周刊發表每周前端技術相關的大事件、文章教程、一些框架的版本更新、以及代碼和工具。每周定期發表,歡迎大家關註、轉載。 如果外鏈不能訪問,關註公眾號「前端每周看」,裡面有解決辦法 大事件 Veni,vidi,formatae! 宣佈Rome Formatter:超快速的 JavaScript 格式化 ...
  • 狀態模式(State Pattern)指允許一個對象在其內部狀態改變時改變它的行為,對象看起來似乎修改了它的類。 一般用來實現狀態機,而狀態機常用在游戲、工作流引擎等系統的開發中: 有限狀態機(Finite State Machine,FSM),狀態機有三個組成部分:狀態(State)、事件(Eve ...
  • 1 .解決跳轉問題:添加一個login方法,跳轉返回一個字元串。 中央控制器DispacherServlet調用EmpController,所以字元串返回給中央控制器。如下圖所示:中央控制器幫我們統一的做 資源的轉發(forward/include) 或 重定向。 1.1 更新 EmpControl ...
  • 在上次反思DDD實踐之後,在類目樹管理項目中再次實踐DDD。從需求分析到建模和具體的落地,結合個人體會,都是乾貨。 ...
  • 抽象工廠模式是什麼 抽象工廠是一種創建型設計模式,用於產品族的構建。它能創建一系列相關的對象, 而無需指定其具體類。 為什麼用抽象工廠模式 在工廠方法模式中具體工廠負責生產具體的產品,每一個具體工廠對應一種具體產品,工廠方法也具有唯一性,一般情況下,一個具體工廠中只有一個工廠方法。但是有時候我們需要 ...
  • Excelize 是 Go 語言編寫的用於操作 Office Excel 文檔基礎庫。2022年4月11日,社區正式發佈了 2.6.0 版本,該版本包含了多項新增功能、錯誤修複和相容性提升優化。下麵是有關該版本更新內容的摘要。 ...
  • 快速上手JWT簽發token和認證,有這一篇就夠了,DRF自帶的和自定義的都幫你總結好了,拿去用~ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...