DRF JWT认证进阶

JWT认证进阶

【0】准备工作

(1)模型准备

  • 模型准备(继承djangoauth_user表)
from django.db import models
from django.contrib.auth.models import AbstractUserclass UserInfo(AbstractUser):mobile = models.CharField(max_length=11, verbose_name='手机号码')
  • 添加配置文件,修改用户模型表为自定义表
AUTH_USER_MODEL = 'app名称.UserInfo'

(2)知识点绑定方法

  • 详情请见:Python 面向对象之绑定和非绑定方法_python 绑定方法-CSDN博客
  • 实例方法
    • 对象调用实例方法时(对象.实例方法()),自动将对象当作第一个参数传入
    • 类调用实例方法时(类.实例方法(类())),需要手动传入一个实例
  • 类方法
    • 类调用类方法时(类.类方法()),自动将类当作第一个参数传入
    • 对象调用类方法时(对象.类方法()),自动将对象的类当作第一个参数传入
  • 简单示例
class A:def __init__(self, name):self.name = name@classmethoddef hi(cls):print(f"{cls.__name__} say hi")def hello(self):print(f"{self.name} say hello")a = A('bruce')
# 实例调用类方法,自定将类传入
a.hi()
# 类调用实例方法,需要手动将实例对象传入
A.hello(a)

(3)保存信息显示中文

  • 在配置文件中注册app就可以
INSTALLED_APPS = ['rest_framework_simplejwt',
]

【1】jwt配置文件

(1)整体查看

  • 这些内容都在from rest_framework_simplejwt import settings可以自己去查看
DEFAULTS = {"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),# 访问令牌的有效期。这里设置为5分钟。"REFRESH_TOKEN_LIFETIME": timedelta(days=1),# 刷新令牌的有效期。这里设置为1天。"ROTATE_REFRESH_TOKENS": False,# 是否每次使用刷新令牌时都生成新的刷新令牌。这里设置为False。"BLACKLIST_AFTER_ROTATION": False,# 刷新令牌在旋转后是否应该被加入黑名单。这里设置为False。"UPDATE_LAST_LOGIN": False,# 是否在每次验证令牌时更新用户的最后登录时间。这里设置为False。"ALGORITHM": "HS256",# 用于签名的算法。这里使用的是HS256。"SIGNING_KEY": settings.SECRET_KEY,# 用于签名的密钥。这里使用的是Django的SECRET_KEY。"VERIFYING_KEY": "",# 用于验证令牌的密钥。这里没有设置。"AUDIENCE": None,# JWT的接收者。这里没有设置。"ISSUER": None,# JWT的签发者。这里没有设置。"JSON_ENCODER": None,# 用于序列化JWT负载的JSON编码器。这里没有设置。"JWK_URL": None,# JSON Web Key Set的URL,用于公钥检索。这里没有设置。"LEEWAY": 0,# 在验证JWT的时间戳时允许的时间误差(秒)。这里设置为0。"AUTH_HEADER_TYPES": ("Bearer",),# 认证头中可以接受的令牌类型。这里设置为只接受"Bearer"类型。"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",# 认证头的名称。这里设置"USER_ID_FIELD": "id",# 用户的ID字段名。这里设置为"id"。"USER_ID_CLAIM": "user_id",# JWT中用户ID的声明名称。这里设置为"user_id"。"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",# 用于验证用户的规则。这里使用的是默认规则。"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),# l令牌类。这里设置为只使用访问令牌。"TOKEN_TYPE_CLAIM": "token_type",# 用于表示用户的模型类。这里使用的是默认模型。"JTI_CLAIM": "jti",#  JWT的JTI(JWT ID)声明名称。这里设置为"jti"。"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",# 用于表示用户的模型类。这里使用的是默认模型。"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",# 滑动刷新令牌中的过期声明名称。这里设置为"refresh_exp"。"SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),# 滑动访问令牌的有效期。这里设置为5分钟。"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),# 滑动刷新令牌的有效期。这里设置为1天。"TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",# 用于获取令牌的序列化器。这里使用默认的序列化器。"TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",# 用于刷新令牌的序列化器。这里使用默认的序列化器。"TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",# 用于验证令牌的序列化器。这里使用默认的序列化器。"TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",# 用于黑名单令牌的序列化器。这里使用默认的序列化器。"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",# 用于获取滑动令牌的序列化器。这里使用默认的序列化器。"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",# 用于刷新滑动令牌的序列化器。这里使用默认的序列化器。"CHECK_REVOKE_TOKEN": False,# 是否检查令牌是否已被撤销。这里设置为False。"REVOKE_TOKEN_CLAIM": "hash_password",# 用于撤销令牌的声明名称。这里设置为"hash_password"。
}

(2)常用的配置

  • ACCESS_TOKEN_LIFETIME: 访问令牌的有效期。这里设置为5分钟。

  • REFRESH_TOKEN_LIFETIME: 刷新令牌的有效期。这里设置为1天。

  • TOKEN_OBTAIN_SERIALIZER:可自定义登录的序列化器

【2】定制登录返回格式

(1)定制整体返回格式

  • jwt默认返回格式是一个字典,包含两个键值对
    • 一个是刷新相关的token,另一个是登录相关的token
  • 我们期望的格式是一个大字典,包含返回自定义状态码code、描述信息msg、用户名username和默认的两个token
    • 所以需要我们自定义,这里的方法是替换默认的登录序列化类,自定义序列化类以实现自定义的返回整体格式
  • 通过默认配置:
    • "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer"
    • 可知道这个类是负责登录序列化的,为了方便,继承它,重写它的方法,添加自定义的属性
    • 通过简单观察源码发现,返回的内容就是validate里面的data,所以只需要在这个data中添加修改一下字段就可以

image-20240419195240224

  • 代码
from rest_framework_simplejwt.serializers import TokenObtainPairSerializerclass JWTTokenPairSerializer(TokenObtainPairSerializer):def validate(self, attrs):data = super().validate(attrs)new_data = {"code": 100,"msg": '登录验证成功',"username": self.user.username,"refresh": data.get("refresh"),"access": data.get("access"),}return new_data
# 配置文件替换默认的序列化类
SIMPLE_JWT ={'TOKEN_OBTAIN_SERIALIZER':'自定义序列化类的位置.JWTTokenPairSerializer'
}# 使用默认的JWT登录
from django.urls import path
from rest_framework_simplejwt.views import token_obtain_pair
urlpatterns = [path('v0/login/', token_obtain_pair),
]

image-20240419200640456

(2)定制payload载荷

  • 默认载荷部分包含了

    • token_type表示这是一个访问令牌(access token)
    • exp是令牌的过期时间(UNIX时间戳)
    • iat是令牌的签发时间
    • jti是令牌的唯一标识符
    • user_id是用户ID
  • 现在思考如何添加自定义字段,比如用户的用户名

    • 还是通过一步一步的找token,最终找到了载荷payload,是一个字典所以可以尝试,像普通字典一样添加新的信息
    • 注意:这里面虽然有类方法,但是无论是self还是super都是实例对象,可以直接调用类方法

PixPin_2024-04-19_20-29-49

  • 代码:还是修改序列化类
from rest_framework_simplejwt.serializers import TokenObtainPairSerializerclass JWTTokenPairSerializer(TokenObtainPairSerializer):@classmethoddef get_token(cls, user):token = super().get_token(user)token['username'] = user.usernamereturn token
  • 检查,添加成功
import base64s = 'token第二段==='
res = base64.b64decode(s.encode('utf8'))
# b'{"token_type":"access","exp":1713528635,"iat":1713528335,"jti":"cc57595cd70f4938baa4673a6f1538ad","user_id":1}
# b'{"token_type":"access","exp":1713530348,"iat":1713530048,"jti":"6807d0722efb41bba01742a44767c18d","user_id":1,"username":"admin"}'

【3】多方式登录

(1)简介

  • 实际的案例中,登录的方式有很多种,既可以是用户名,还可以是手机号,还可以是邮箱等,并且他们的登录输入入口都是一个,所以这里将实现这种接口

image-20240419203858725

(2)登录视图函数和路由

  • 序列化类有检验功能,所以重心不在这里
# 路由层
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from .views import JWTLoginAPIViewrouter = SimpleRouter()
router.register('', JWTLoginAPIView, basename='login')
urlpatterns = [path('v1/', include(router.urls)),
]
# 视图层
from rest_framework.viewsets import GenericViewSet
from .serializer import JWTTokenSerializer
from rest_framework.response import Response
from rest_framework.decorators import actionclass JWTLoginAPIView(GenericViewSet):serializer_class = JWTTokenSerializer@action(methods=['post'], detail=False)def login(self, request, *args, **kwargs):ser = self.get_serializer(data=request.data)if ser.is_valid():refresh = ser.context.get('refresh')access = ser.context.get('access')return Response({"code": 100, "msg": "登录验证成功", "refresh": refresh, "access": access})return Response({"code": 100, "msg": ser.erros})

(3)序列化类

  • 序列化类的检验分为三个过程,首先是字段参数(包括validators),然后是局部钩子,最后是全局钩子
  • 由于需要校验用户输入字段和密码字段,所以使用全局钩子
  • 整体逻辑
    • 使用不同方法区分传入的值是电话,还是邮箱,还是手机号
    • 由于使用的是django的用户表,所以要用相应的加密方法判断密码
    • 在定制载荷payload中,知道了RefreshToken处理token,所以要调用这个类的类方法for_user处理
import refrom rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework_simplejwt.tokens import RefreshToken
from .models import UserInfoclass JWTTokenSerializer(serializers.Serializer):username = serializers.CharField()password = serializers.CharField()def validate(self, attrs):username = attrs.get('username')password = attrs.get('password')if re.match(r'^1[3-9][0-9]{9}$', username):user = UserInfo.objects.filter(username=username).first()elif re.match(r"/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/", username):user = UserInfo.objects.filter(username=username).first()else:user = UserInfo.objects.filter(username=username).first()# 使用的是django的表,需要使用响应的方法进行密码检验if user and user.check_password(password):# 获取tokentoken = RefreshToken.for_user(user)self.context['refresh'] = str(token)self.context['access'] = str(token.access_token)return attrsraise ValidationError("用户名或者密码错误")

image-20240419212802362

【4】自定义用户表、手动签发和认证

(1)模型表

  • 用户表用普通的表
  • 创建一个数据表用于侧式认证功能
from django.db import modelsclass UserNormal(models.Model):username = models.CharField(max_length=64, verbose_name='用户名')password = models.CharField(max_length=64, verbose_name='密码')class Book(models.Model):name = models.CharField(max_length=64, verbose_name="书名")price = models.CharField(max_length=64, verbose_name="价格")publish = models.CharField(max_length=64, verbose_name="出版社")

(2)路由层

  • 自动路由
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from .views import JWTLoginAPIView, BookAPIViewrouter = SimpleRouter()
router.register('', JWTLoginAPIView, basename='login')
router.register('book', BookAPIView, basename='book')
urlpatterns = [path('v1/', include(router.urls)),
]

(3)视图层

  • 和之前没有区别,多创一个视图类用于测试
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from .serializer import JWTTokenSerializer, BookModelSerializer
from rest_framework.response import Response
from rest_framework.decorators import action
from .models import Book
from .authentication import JWTAuthclass BookAPIView(ModelViewSet):queryset = Book.objects.all()serializer_class = BookModelSerializerauthentication_classes = [JWTAuth]class JWTLoginAPIView(GenericViewSet):serializer_class = JWTTokenSerializer@action(methods=['post'], detail=False)def login(self, request):ser = self.get_serializer(data=request.data)if ser.is_valid():refresh = ser.context.get('refresh')access = ser.context.get('access')return Response({"code": 100, "msg": "登录验证成功", "refresh": refresh, "access": access})return Response({"code": 100, "msg": ser.errors})

(4)序列化

import refrom rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework_simplejwt.tokens import RefreshToken
from .models import UserInfo, Book, UserNormalclass BookModelSerializer(serializers.ModelSerializer):class Meta:model = Bookfields = "__all__"class JWTTokenSerializer(serializers.Serializer):username = serializers.CharField()password = serializers.CharField()def validate(self, attrs):username = attrs.get('username')password = attrs.get('password')user = UserNormal.objects.filter(username=username, password=password).first()if user:# 获取tokentoken = RefreshToken.for_user(user)self.context['access'] = str(token.access_token)self.context['refresh'] = str(token)return attrsraise ValidationError("用户名或者密码错误")

(5)认证类

from rest_framework_simplejwt.authentication import JWTAuthentication
from .models import UserNormal
from rest_framework.exceptions import AuthenticationFailedclass JWTAuth(JWTAuthentication):def authenticate(self, request):token = request.META.get("HTTP_AUTHORIZATION")if token:# 调用父类的方法验证token,内部会自动验证抛出异常validated_token = super().get_validated_token(token)# 放入的默认信息是user_iduser_id = validated_token.get('user_id')user = UserNormal.objects.filter(pk=user_id)return user, tokenelse:raise AuthenticationFailed("登录认证信息认证失败")

(6)测试

  • 注意:上面调用的方法,不可以在token的前面带任何参数

image-20240419233202680

  • 解决办法:
    • 使用父类的获取原生token方法,这样仍将携带配置文件中的要求(Bearer,也可以自定义其他的)
header = self.get_header(request)
token = self.get_raw_token(header)

image-20240419234023749

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/3847.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言——内存函数的实现与模拟

1. memcpy 函数 与strcpy 函数类似 1.头文件 <string.h> 2.基本格式 • 函数memcpy从source的位置开始向后复制num个 字节 的数据到destination指向的内存位置。 • 这个函数在遇到 \0 的时候并不会停下来。 • 如果source和destination有任何的重叠&#xff0…

2024年钉钉直播回放怎么下载

又到了2024年,最近钉钉迎来了一波更新,经过我的研究,总算研究出来了一个方法,并且做成了工具 首先&#xff0c;让我们了解一下钉钉直播回放的下载方法。 钉钉直播回放工具链接&#xff1a;https://pan.baidu.com/s/1oPWJOp8L2SBDlklt_t5WQQ?pwd1234 提取码&#xff1a;1234 -…

【快速上手ESP32(基于ESP-IDFVSCode)】10-事件循环WiFi

事件循环 本来这篇文章是只写WiFi的&#xff0c;但是写的时候才发现离不开事件循环&#xff0c;因此再多添一点内容在WiFi前面。 事件循环简单来说就是一个&#xff08;循&#xff09;环&#xff0c;我们可以在这个环上绑上一些事件&#xff0c;我们也可以监听这个环&#xf…

JavaScript进阶(十五):JS 垃圾回收机制_vue gc

内存&#xff1a;由可读写单元组成&#xff0c;表示一片可操作空间&#xff1b;管理&#xff1a;人为的去操作一片空间的申请、使用和释放&#xff1b;内存管理&#xff1a;开发者主动申请空间、使用空间、释放空间&#xff1b;管理流程&#xff1a;申请-使用-释放&#xff1b;…

oracle sql monitor简单使用说明

一 sql monitor介绍 二 用命令行方式生成sql monitor报告 set long 1000000 set longchunksize 100000 set linesize 1000 set pagesize 0 set trim on set trimspool on set echo off set feedback off spool report_sql_monitor.html select dbms_sqltune.report_s…

线性代数-行列式-p1 矩阵的秩

目录 1.定义 2. 计算矩阵的秩 3. 矩阵的秩性质 1.定义 2. 计算矩阵的秩 3. 矩阵的秩性质

美国言语听力学会(ASHA)关于非处方 (OTC) 助听器的媒体声明(翻译稿)

美国国会于 2021 年 4 月 13 日批准美国听力学会积极提供建议&#xff0c;并一直积极参与制定FDA关于非处方助听器销售的拟议法规。根据2017年通过的立法授权。学院积极参与帮助塑造授权立法&#xff0c;并就即将出台的条例分享了建议。 根据美国卫生与公众服务部NIH / NIDCD的…

用Python绘制了几张有趣的可视化图表

流程图存在于我们生活的方方面面&#xff0c;对于我们追踪项目的进展&#xff0c;做出各种事情的决策都有着巨大的帮助&#xff0c;而对于的Python而言呢&#xff0c;绘制流程图也是十分轻松的&#xff0c;今天小编就来为大家介绍两个用于绘制流程图的模块&#xff0c;我们先来…

12 JavaScript学习: 字符串

JavaScript 字符串 JavaScript 字符串是一种用于存储和操作文本数据的数据类型。字符串可以包含字母、数字、符号和空格等字符。在 JavaScript 中&#xff0c;字符串可以使用单引号&#xff08;&#xff09;或双引号&#xff08;"&#xff09;来定义。 例如&#xff1a;…

链表与模拟LinkedList的实现

1. ArrayList的缺陷 ArrayList底层使用数组来存储元素 由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后 搬移&#xff0c;时间复杂度为O(n)&#xff0c;效率比较低。因此ArrayList不适合做任意位…

机械臂过程

rosdep install --from-paths src --ignore-src --rosdistro melodic0、安装机械手臂 官方教程&#xff1a; 前人教程&#xff1a;UR5机械臂仿真实例 rosdep update 出错&#xff0c;使用小鱼的大佬的 一键配置 wget http://fishros.com/install -O fishros && . fish…

Rest微服务案例

Rest 父工程构建microservicecloud-api公共子模块Modulemicroservicecloud-provider-dept-8001部门微服务提供者Modulemicroservicecloud-consumer-dept-80部门微服务消费者Module 以Dept部门模块做一个微服务通用案例 Consumer消费者&#xff08;Client&#xff09;通过REST调…

ssm082基于java斗车交易系统设计与实现+vue

斗车交易系统 摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&…

第三篇:Python编程基础:掌握核心语法与开发技巧

Python编程基础&#xff1a;掌握核心语法与开发技巧 1 引言 在这个信息化迅速蔓延的世界中&#xff0c;Python语言如同钥匙一般开启了通往各种可能性的大门。无论你是数据科学家、网络工程师、机器学习专家&#xff0c;还是仅仅对自动化办公感兴趣的办公室人员&#xff0c;Pyt…

环境配置——Windows平台配置VScode运行环境为远程服务器或虚拟机

1. 远程机需要先安装SSH服务&#xff0c;命令如下 sudo apt install openssh-server 2. 安装好后需要开启SSH服务&#xff1a; sudo service sshd start 3. 查看SSH服务是否有被开启&#xff1a; sudo systemctl status sshd.service 4. 本地Windows需要生成密钥将公钥放…

CTFshow-PWN-栈溢出(pwn36)

存在后门函数&#xff0c;如何利用&#xff1f; 好好好&#xff0c;终于到了这种有后门函数的了 checksec 检查一下&#xff1a; 32 位程序&#xff0c;RELRO 保护部分开启 RWX: Has RWX segments 存在可读可写可执行的段 使用 ida32 看 main 函数 跟进 ctfshow 函数…

go语言并发实战——日志收集系统(十) 重构tailfile模块实现同时监控多个日志文件

前言 在上一篇文章中&#xff0c;我们实现了通过etcd来同时指定多个不同的有关分区与日志文件的路径&#xff0c;但是锁着一次读取配置的增多&#xff0c;不可避免的出现了一个问题&#xff1a;我们如何来监控多个日志文件&#xff0c;这样原来的tailFile模块相对于当下场景就…

Golang | Leetcode Golang题解之第46题全排列

题目&#xff1a; 题解&#xff1a; func permute(nums []int) [][]int {var (n len(nums)dfs func(vals []int) // 已选择数 排列为vals 后续回溯继续选择 直至选完ans [][]int)dfs func(vals []int) {//边界if len(vals) n {ans append(ans, vals)}//转移 枚举选哪个f…

嵌入式系统中的实时操作系统(RTOS)深入应用与优化

引言 实时操作系统&#xff08;RTOS&#xff09;在嵌入式系统中扮演着至关重要的角色&#xff0c;特别是在需要快速响应和高度可靠性的应用中。 我将探讨如何在STM32单片机上实现RTOS&#xff0c;包括任务管理、内存管理以及中断处理&#xff0c;以提高系统的效率和响应速度。…

github+PicGo+obsidian来作为你的免费高效可靠图床吧

前提 一直以来 博客的图床问题都是个大问题 ,如何找到一个 可靠并且 方便的搭建方式 非常重要 今天介绍一种 githubpicGoobsidian的搭建方式 准备github库 生成个人github token 找到个人 设置 生成一个新token 或者已经有的直接用 新生成的token 需要记录下来 这可能是你最后…