当前位置: 首页 > news >正文

Django 学习日记(补充1)| 彻底吃透:自定义 JWT 认证 + 全局登录中间件

大家好,这是我 Django 学习日记的第三篇。上一篇我们把路由、反向解析、DRF 自动路由、媒体文件、跨域全部讲明白了。今天我们进入整个项目最核心、最安全、最关键的部分:用户登录认证体系(在进入视图前的一篇补充文章)

本文将从JWT 是什么手写 JWT 生成自定义 DRF 认证类自定义全局登录中间件白名单、匿名用户、异常处理,全部逐行拆解。你开发中遇到的登录、token、中间件问题,这一篇全部解决。

一、本篇你能学到什么

  1. JWT 登录令牌如何生成
  2. 自定义 DRF JWT 认证类(逐行讲解)
  3. 自定义 Django 全局登录中间件(企业级实战)
  4. 整个登录认证完整流程

二、什么是 JWT?为什么要用它?

一句话总结:JWT(JSON Web Token)就是前端的 “登录身份证”。

前后端分离项目中,服务器不保存 session,所以必须给前端发一个令牌:

  • 登录成功 → 发放 JWT
  • 后续请求 → 携带 JWT
  • 服务器验证 JWT → 确认身份

JWT 长这样:

plaintext

aaaaa.bbbbb.ccccc

分为三部分:头部(声明加密算法)、载荷(存放自定义信息)、签名(对前两部分加密,防止篡改)。


三、第一步:手写 JWT 生成函数

我们先手写一个工具函数,登录成功后生成 token

代码位置

apps/oaauth/authentications.py

python

运行

import jwt import time from django.conf import settings # 生成 JWT Token def generate_jwt(user): # 过期时间:7天 expire_time = time.time() + 60 * 60 * 24 * 7 # 传入用户ID + 过期时间 + 项目密钥(传入自定义内容,即载荷) return jwt.encode( {"userid": user.pk, "exp": expire_time}, key=settings.SECRET_KEY )

关键讲解

  1. 返回格式是标准 JWT,不是自定义
  2. userid 是我们自己存在 token 里的
  3. exp 是 JWT 标准过期字段
  4. 必须使用 SECRET_KEY 密钥签名

四、第二步:自定义 DRF JWT 认证类

这是 DRF 官方规范的认证类,专门验证 JWT 是否合法

全代码

import jwt import time from django.conf import settings from rest_framework.authentication import BaseAuthentication, get_authorization_header, TokenAuthentication from rest_framework import exceptions from jwt.exceptions import ExpiredSignatureError from .models import OAUser#这里根据自己的项目来定义,我的用户模型定义为OAUser def generate_jwt(user): expire_time = time.time() + 60 * 60 * 24 * 7 return jwt.encode({"userid": user.pk, "exp": expire_time}, key=settings.SECRET_KEY) class UserTokenAuthentication(BaseAuthentication): def authenticate(self, request): # 这里的request:是rest_framework.request.Request对象 # 这里最后返回的是Django的原生request请求而不是DRF封装过的request请求 return request._request.user, request._request.auth class JWTAuthentication(BaseAuthentication): #定义关键字 keyword = 'JWT' def authenticate(self, request): #JWT令牌内容为JWT+空格+生成的token auth = get_authorization_header(request).split() #如果令牌格式不合法或者不存在JWT令牌,不做JWT验证,直接放行,由后续流程统一处理 if not auth or auth[0].lower() != self.keyword.lower().encode(): return None if len(auth) == 1: msg = "不可用的JWT请求头!" raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: msg = '不可用的JWT请求头!JWT Token中间不应该有空格!' raise exceptions.AuthenticationFailed(msg) try: jwt_token = auth[1] jwt_info = jwt.decode(jwt_token, settings.SECRET_KEY, algorithms='HS256') userid = jwt_info.get('userid') try: # 绑定当前user到request对象上 user = OAUser.objects.get(pk=userid) setattr(request,'user', user) return user, jwt_token except: msg = '用户不存在!' raise exceptions.AuthenticationFailed(msg) except ExpiredSignatureError: msg = "JWT Token已过期!" raise exceptions.AuthenticationFailed(msg)

可能的疑问

1. auth = get_authorization_header(request).split()

获取请求头并分割成:["JWT", "token字符串"]

2. if not auth or ... return None

不是报错!是跳过!交给下一个认证类处理!

3. 为什么 len (auth) 必须等于 2?

因为格式必须是:JWT xxxxxxx

4. jwt.decode 三个参数干什么?

  • jwt_token:要解码的 token
  • SECRET_KEY:必须用项目密钥验证防篡改
  • HS256:加密算法

5. return user, jwt_token

Python 支持返回多个值 → 本质返回元组DRF 认证类必须这样返回!


五、第三步:桥接中间件与 DRF 的认证类

class UserTokenAuthentication(BaseAuthentication): def authenticate(self, request): # 直接拿中间件已经验证好的用户 return request._request.user, request._request.auth ''' UserTokenAuthentication 这个类的作用: 将 DRF 包装的 request 对象, 降级获取到 Django 原生 request 对象, 从中取出中间件已经验证好的 user 和 auth, 返回给 DRF 框架使用。 '''

核心讲解

在 DRF 框架中,视图收到的 request 并不是 Django 原生 request,而是 DRF 经过包装、增强后的rest_framework.request.Request对象。

但我们的登录中间件是 Django 层面的中间件,它把登录用户绑定到了原生 Django request 对象上。因此,我们需要一个 “桥接” 的认证类

这个类不做任何验证,只负责 “桥接”:从原生 request 中取出用户 → 交给 DRF 认证体系。


六、最核心:自定义 Django 全局登录中间件

这是本篇最重点、最实战、企业级必用的部分。

代码位置

apps/oaauth/middlewares.py

完整代码

from django.utils.deprecation import MiddlewareMixin from rest_framework.authentication import get_authorization_header from rest_framework import exceptions import jwt from django.conf import settings from django.contrib.auth import get_user_model from django.http.response import JsonResponse from rest_framework.status import HTTP_403_FORBIDDEN from jwt.exceptions import ExpiredSignatureError from django.contrib.auth.models import AnonymousUser OAUser = get_user_model() class LoginCheckMiddleware(MiddlewareMixin): keyword = "JWT" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 白名单:不需要登录就能访问 self.white_list = ['/auth/login', '/staff/active'] #先判断是否有令牌,有则进行令牌校验,否则直接抛出异常(因为没令牌说明没登录) def process_view(self, request, view_func, view_args, view_kwargs): # 白名单判断 if request.path in self.white_list: request.user = AnonymousUser() request.auth = None return None # 开始校验 JWT(try 里面全部是登录校验) try: auth = get_authorization_header(request).split() if not auth or auth[0].lower() != self.keyword.lower().encode(): raise exceptions.ValidationError("请传入JWT!") # 格式判断 if len(auth) == 1: raise exceptions.AuthenticationFailed("无效的JWT请求头") elif len(auth) > 2: raise exceptions.AuthenticationFailed("JWT不能包含空格") # 解码 jwt_token = auth[1] jwt_info = jwt.decode(jwt_token, settings.SECRET_KEY, algorithms=['HS256']) userid = jwt_info.get('userid') user = OAUser.objects.get(pk=userid) # 绑定用户 request.user = user request.auth = jwt_token # 验证失败 → 未登录 except Exception as e: return JsonResponse({"detail": "请先登录!"}, status=HTTP_403_FORBIDDEN)

七、中间件逐行拆解

1. 为什么中间件要放在 oaauth 用户模块?

因为:

  • 登录认证属于用户模块职责
  • 中间件强依赖用户模型
  • 企业级规范:谁管理登录,中间件就放谁那

2. 为什么必须用 get_user_model ()?

因为:中间件加载时,模型还未初始化,直接导入会报错!

3.init方法干什么?

初始化白名单登录页面、激活页面必须放行,否则永远无法登录。白名单在 __init__ 中只初始化一次,提高效率。

4. super().init干什么?

调用父类构造方法,必须写!不写中间件报错!

5. 白名单代码到底干什么?

if request.path in self.white_list: request.user = AnonymousUser() request.auth = None return None

讲解

  • 白名单接口不验证登录
  • 必须设置匿名用户,保证视图 request.user 一定存在
  • return None →放行,继续执行视图

6. 为什么要匿名用户?

Django 所有视图默认request.user一定存在!不设置会直接崩溃。

7. try 里面的代码干什么?

对非白名单接口进行 JWT 登录校验!

8. try 失败为什么代表未登录?

因为:没有 Token / Token 错误 / Token 过期 = 未登录

9. 为什么返回 JsonResponse?

给前端返回标准 JSON 错误提示


八、整个登录认证执行流程

前端请求 → 中间件

  1. 白名单→ 放行
  2. 非白名单→ JWT 校验
  3. 校验成功→ 绑定用户
  4. 校验失败→ 返回请先登录

中间件通过 → DRF 认证类

  1. 直接拿中间件的用户
  2. DRF 认可登录状态

九、中间件注册(必须配置)

settings.py

MIDDLEWARE = [ ... 'apps.oaauth.middlewares.LoginCheckMiddleware',#放在所有中间件最后 ]

十、DRF 认证配置

python

运行

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'apps.oaauth.authentications.UserTokenAuthentication', ] }

十一、本篇总结

  1. JWT 就是登录身份证
  2. generate_jwt 生成令牌
  3. JWTAuthentication 验证令牌
  4. 中间件做全局登录拦截
  5. 白名单放行登录接口
  6. 匿名用户保证视图不崩溃
  7. return None 代表放行

我的分享到这里就结束了

如有错误,欢迎指正

http://www.jsqmd.com/news/542985/

相关文章:

  • 2026年多模态AI前瞻:Qwen3-VL-2B开源生态发展潜力分析
  • 次元画室快速上手:用对话方式打造你的二次元角色
  • RTX 4090显卡福利:Qwen2.5-VL-7B-Instruct轻量化部署,支持对话历史管理
  • SDMatte+边缘精修教程:利用Alpha通道二次调整、PS中细化羽化与收缩参数
  • leetcode 困难题 1505. 最多 K 次交换相邻数位后得到的最小整数
  • WeMod Pro免费解锁终极指南:两种补丁方法完整对比与实战教程
  • 3个高级技巧:用ScintillaNET构建专业级文本编辑器的实战指南
  • SDMatte电商ROI测算:单图处理成本0.008元,较外包节省92%费用
  • 从一次线上OOM到MySQL锁表:我是如何用dmesg、jstack和jvisualvm揪出连环故障的
  • Miro收购Reforge,助力企业顺利迈向人工智能时代转型
  • FireRed-OCR保姆级教程:一键部署,精准提取表格公式转Markdown
  • Qwen3-VL历史文物识别:博物馆数字化管理部署解决方案
  • 77.基于matlab-GUI的图像分割分别包括超像素 (superpixels)分割 SLIC算法
  • 2026年最佳SaaS联盟营销平台:启动SaaS联盟计划
  • GLM-4-9B-Chat-1M保姆级部署指南:vLLM+Chainlit前端一键调用
  • NaViL-9B实战手册:从零部署到生产环境监控的全流程技术文档
  • 硬件知识总结梳理-4(磁珠)
  • NaViL-9B实战手册:健康检查API与服务异常定位全流程
  • AI资讯速递 - 2026-03-27
  • 循环神经网络 (七)双向 RNN 与深层 RNN
  • Wan2.1-umt5与STM32CubeMX:嵌入式AI项目初始化配置联想
  • 智能协作:Krita AI图像生成插件的创作革命
  • 算法认知战:用垃圾信息污染AI训练数据
  • vLLM-v0.17.1入门必看:vLLM Serving API参数详解与最佳实践
  • NaViL-9B图文理解入门:支持中英文混合提问的实测案例
  • SOONet与Transformer架构深度解析:提升长视频理解精度的核心技术
  • CSC荣获全球信息安全奖“尖端证书生命周期管理”奖
  • SenseVoice-small-onnx REST API开发手册:curl+Python调用+健康检查全解析
  • 番茄小说下载器终极指南:打造你的私人离线阅读库
  • RWKV7-1.5B-G1A集成Python爬虫:自动化数据采集与智能分析实战