Vue 和 Django 實現 Token 身份驗證

来源:https://www.cnblogs.com/zewanhuang/archive/2022/08/17/16595881.html
-Advertisement-
Play Games

使用 Django 編寫的 B/S 應用通常會使用 Cookie + Session 的方式來做身份驗證,用戶登錄信息存儲在後臺資料庫中,前端 Cookie 也會存儲少量用於身份核驗的數據,由後臺直接寫入。但是在開發調試階段,使用 Postman 等請求工具請求登錄時,可能會缺失前端本應存儲的數據,... ...


使用 Django 編寫的 B/S 應用通常會使用 Cookie + Session 的方式來做身份驗證,用戶登錄信息存儲在後臺資料庫中,前端 Cookie 也會存儲少量用於身份核驗的數據,由後臺直接寫入。但是在開發調試階段,使用 Postman 等請求工具請求登錄時,可能會缺失前端本應存儲的數據,而導致登錄信息核驗一直不成功。在本地聯調前後端時可能也會有問題。

本篇介紹基於 Token 的身份驗證機制,並使用 Vue 和 Django 實現。

基於 Token 的驗證流程

與 Session 不同的是,Token 機制不會將用戶登錄信息存儲在後臺資料庫中,而是生成含有身份信息的 Token 字元串存儲在前端中。在前端請求需要驗證的後臺 API 時,後端將優先攔截並核驗身份信息。

基於 Token 的驗證流程如下:

  1. 客戶端使用用戶名和密碼請求登錄
  2. 伺服器收到請求後,驗證用戶名和密碼
  3. 驗證成功後,服務端根據用戶信息簽發一個 Token,返回給客戶端
  4. 客戶端存儲 Token
  5. 客戶端每次向伺服器發送其它請求時,都要攜帶 Token
  6. 伺服器收到請求,若請求的 API 需要驗證身份,則先驗證 Token,成功後再返回數據
token.svg

Token 的組成

構造 Token 的方法較多,只要客戶端和服務端約定好了生成和驗證的格式,則有很多自定義的方法。當然,也有一些標準的寫法,例如 JWT,讀作 /jot/,表示 JSON Web Tokens。

JWT 標準的 Token 有三個部分:

  • header
  • payload
  • signature

三個部分使用 . 分隔開,且使用 Base64 編碼,示例如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

Header 主要蘊含兩部分內容的信息,分別是 Token 的類型和加密使用的方法。

初始數據對象示例如下:

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

上述數據對象在經過演算法加密、Base64 編碼後,變為 Token 的第一部分:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

Payload 為 Token 的具體內容,下麵是可選的標準欄位,也可以自定義添加需要的內容。

  • iss: Issuer, 發行者
  • sub: Subject, 主題
  • aud: Audience, 觀眾
  • exp: Expiration time, 過期時間, 可為時間戳格式
  • nbf: Not before
  • iat: Issued at, 發行時間, 可為時間戳格式
  • jti: JWT ID

同樣的,該部分初始數據對象經過演算法加密、Base64 編碼後,變為 Token 的第二部分。

Signature

Signature 為 Token 的簽名部分,相當於是前兩部分的簽名,用於防止其他人篡改 Token 中的信息。在處理時,可以將生成的 Token 前兩段內容,使用 MD5 等簽名演算法進行處理,將結果作為本部分內容。

加密演算法

從上面對 Token 組成部分的介紹中,可以瞭解到,在規定 Token 需蘊含的數據信息後,需要經過一定的演算法加密、Base64 編碼後成為 Token 的第一、二部分。因此,在生成 Token 時,要解決使用什麼加密演算法。

此處的加密演算法一定要是可逆的、可解密的,因為我們不僅要生成 Token,還要能從 Token 中解析出我們生成時存儲的數據,以驗證用戶信息和 Token 的有效期。因此,這裡不能採用 MD5、SHA1 這樣的哈希演算法,因為它們無法解密,只能用於生成簽名。

在 Django 中內置了加密模塊 django.core.signing,我們調用其中的 dumpsloads 函數實現加密和解密。

示例:

from django.core import signing
data = {
    "username": "Zewan"
}
value = signing.dumps(data) # encrypt
raw = signing.loads(value)  # decrypt
print(value, src)

Django 生成和驗證 Token

上面我們已經瞭解了 Token 機制的流程和採取的加密演算法,接下來介紹 Django 中如何編寫代碼以實現 Token 機制。

我規定 Token 的 Header 部分為 {"typ": "JWP", "alg": "default"},Payload 部分含有用戶名 username 和過期時間 exp,Signature 我使用 MD5 演算法生成簽名。在登錄成功後,後端返回給前端 username 和 Token,由前端存儲起來;當前端發送需要驗證身份信息的請求時,將 username 和 Token 加入請求頭中,後端從請求頭獲取這兩部分,從 Token 中解析得到用戶名和過期時間,核驗請求頭中的 username 是否正確及 Token 是否有效。

處理 Token

我在 utils/token.py 文件中實現 Token 的生成和解析數據的功能:

import time
from django.core import signing
import hashlib

HEADER = {'typ': 'JWP', 'alg': 'default'}
KEY = "Zewan"
SALT = "blog.zewan.cc"

def encrypt(obj):
    """加密:signing 加密 and Base64 編碼"""
    value = signing.dumps(obj, key=KEY, salt=SALT)
    value = signing.b64_encode(value.encode()).decode()
    return value

def decrypt(src):
    """解密:Base64 解碼 and signing 解密"""
    src = signing.b64_decode(src.encode()).decode()
    raw = signing.loads(src, key=KEY, salt=SALT)
    return raw

def create_token(username):
    """生成token信息"""
    # 1. 加密頭信息
    header = encrypt(HEADER)
    # 2. 構造Payload(有效期14天)
    payload = {"username": username, "iat": time.time(), 
               "exp": time.time()+1209600.0}
    payload = encrypt(payload)
    # 3. MD5 生成簽名
    md5 = hashlib.md5()
    md5.update(("%s.%s" % (header, payload)).encode())
    signature = md5.hexdigest()
    token = "%s.%s.%s" % (header, payload, signature)
    return token

def get_payload(token):
    """解析 token 獲取 payload 數據"""
    payload = str(token).split('.')[1]
    payload = decrypt(payload)
    return payload

def get_username(token):
    """解析 token 獲取 username"""
    payload = get_payload(token)
    return payload['username']

def get_exp_time(token):
    """解析 token 獲取過期時間"""
    payload = get_payload(token)
    return payload['exp']

def check_token(username, token):
    """驗證 token:檢查 username 和 token 是否一致且未過期"""
    return get_username(token) == username and get_exp_time(token) > time.time()

登錄成功批發 Token

在登錄請求處理函數中,驗證用戶名和密碼成功後,調用 utils/token.py 文件中的 create_token 函數生成 token,並將 username 和 token 返回前端。代碼較為簡單,此處不多展示。

中間件攔截驗證 (Middleware)

創建一個中間件(Middleware),在前端請求需要身份核驗的後端路由時,由該中間件核驗其 username 和 token,驗證成功後再放行,進入業務處理的 API 中。

我的項目名稱為 backend_demo,在自動生成的 backend_demo 包中,我創建 middleware.py 文件,構建中間件。該文件內容如下:

提示:前端向請求頭添加 xxx 信息,一般會自動轉變為 HTTP_XXX (全部大寫)

from utils.token import check_token
from django.http import JsonResponse

try:
    from django.utils.deprecation import MiddlewareMixin  # Django 1.10.x
except ImportError:
    MiddlewareMixin = object

# 白名單,表示請求裡面的路由時不驗證登錄信息
API_WHITELIST = ["/api/user/login", "/api/user/register"]

class AuthorizeMiddleware(MiddlewareMixin):
    def process_request(self, request):
        if request.path not in API_WHITELIST:
            # 從請求頭中獲取 username 和 token
            username = request.META.get('HTTP_USERNAME')
            token = request.META.get('HTTP_AUTHORIZATION')
            if username is None or token is None:
                return JsonResponse({'errno': 100001, 'msg': "未查詢到登錄信息"})
            else:
                # 調用 check_token 函數驗證
                if check_token(username, token):
                    pass
                else:
                    return JsonResponse({'errno': 100002, 
                                         'msg': "登錄信息錯誤或已過期"})

實現中間件後,將其添加進項目中,在 settings.py 文件的 MIDDLEWARE 中添加建立的中間件:

MIDDLEWARE = [
    'backend_demo.middleware.AuthorizeMiddleware',
    # ...
]

Vue 存儲和攜帶 Token

登錄成功存儲 Token

登錄成功後,前端獲取並存儲後端返回的 username 和 token。前端實現存儲的方式有很多,我這裡使用簡單的方法,將其存儲在 localStorage 中。

// 此處用 username 和 authorization 表示,放到項目中要依據情況修改該變數標識
localStorage.setItem("username", username);
localStorage.setItem("authorization", authorization);

請求頭攜帶用戶名和 Token

接下來實現請求頭攜帶用戶名和 Token 信息。

在 Vue.js 實現的項目中,我一般是用 Axios 向後端發送請求。在 Axios 中,不需要在每一處請求的代碼中添加請求頭代碼,只需要在 main.js 中配置 Axios 的預設請求器,即可使所有的請求中 headers 都攜帶用戶名和 token。

main.js 中核心代碼如下:

提示:這裡填入 headers 中雖然是 usernameauthorization,但會被自動轉化為 HTTP_USERNAMEHTTP_AUTHORIZATION

import axios from 'axios';

// add username and token into headers
axios.interceptors.request.use(
    config => {
        var username = localStorage.getItem('username');
        var authorization = localStorage.getItem('authorization');
        // 若 localStorage 中含有這兩個欄位,則添加入請求頭
        if (username & authorization) {
            config.headers.authorization = authorization;
            config.headers.username = username;
        }
        return config;
    },
    error => {
        return Promise.reject(error);
    }
);

這樣,就在 Vue.js 和 Django 編寫的前後端項目中,實現了基於 Token 的身份驗證機制。其他前後端框架的 Token 實現原理與本文一致,但是代碼需要根據所用框架進行合理修改。


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

-Advertisement-
Play Games
更多相關文章
  • Oracle 備份與恢復 (Docker部署版) 一,宿主機設置定時備份腳本 1.檢查Oracle容器是否正常運行 docker ps 2.進入容器,創建shell腳本 #oracle11g 是容器名 docker exec -it oracle11g bash #創建資料庫dump的目錄 mkdi ...
  • StoneDB 的整體架構分為三層,分別是應用層、服務層和存儲引擎層。應用層主要負責客戶端的連接管理和許可權驗證;服務層提供了 SQL 介面、查詢緩存、解析器、優化器、執行器等組件;Tianmu 引擎所在的存儲引擎層是 StoneDB 的核心,數據的組織和壓縮、以及基於知識網格的查詢優化均是在 Tia ...
  • 1.概述 說起垃圾收集(Garbage Collection, GC),大部分人都把這項技術當做Java語言的伴生產物。事實上,GC的歷史遠遠比Java久遠,1960年誕生於MIT的Lisp是第一門 真正使用記憶體動態分配和垃圾收集技術的語言。當Lisp還在胚胎時期時,人們就在思考 GC需要完成的三件 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。 ...
  • 1. 運行時數據區域 Java虛擬機在執行Java程式的過程中會把它所管理的記憶體劃分為若幹個不同的數據 區域。這些區域都有各自的用途,以及創建和銷毀的時間,有的區域隨著虛擬機進程的 啟動而存在,有些區域則是依賴用戶線程的啟動和結束而建立和銷毀。根據《Java虛擬 機規範(第2版)》的規定,Java虛 ...
  • Java集合05 11.HashSet課堂練習 11.1課堂練習1 定義一個Employee類,該類包括:private成員屬性name,age 要求: 創建3個Employee對象放入HashSet中 當name和age的值相同時,認為是相同員工,不能添加到HashSet集合中 思路:不同對象的哈 ...
  • Spring5相關的知識點,IOC,AOP,DI依賴註入,Bean的自動裝配,使用註解開發,spring整合mybatis等於Spring相關的知識都在這裡了 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...