simple-jwt快速入门(包含自定制)

simple-jwt快速入门(包含自定制)

目录

  • simple-jwt快速入门(包含自定制)
    • 安装
    • 路由层
    • 视图层
    • 全局配置
    • 前端传入参数
    • 配置文件
    • 定制登录返回格式
    • 定制payload格式
    • 自定制签发-认证

安装

pip install djangorestframework-simplejwt

路由层

from rest_framework_simplejwt.views import TokenObtainPairViewurlpatterns = [path('login/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
]
  • 浏览器路径随便填,但是必须要调用TokenObtainPairView
  • TokenObtainPairView:登录接口
  • TokenRefreshView:刷新token
  • TokenVerifyView:校验token

视图层

# views.py
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthenticationclass UserDetail(ModelViewSet):authentication_classes = [JWTAuthentication]permission_classes = [IsAuthenticated]
  • 在需要登录才能访问的视图类中加入JWTAuthenticationIsAuthenticated验证

  • 此时相当于用DRF自带的验证视图,他会根据django自带的auth表为我们进行校验,并返回access和refresh

全局配置

REST_FRAMEWORK = {# DRF默认权限类'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),# DRF默认认证类'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_simplejwt.authentication.JWTAuthentication',),
}

前端传入参数

由于simple-jwt会默认校验auth表中的数据,因此不需要再额外编写视图类,直接传入参数即可(参数要和数据库字段一致)

image-20240419220750941

  • access:校验用的信息,在访问需要登录才能使用的视图方法时需要将它放入浏览器

image-20240419220940947

  • Key:必须填Authorization
  • Value:必须填Bearer+空格+access参数

配置文件

simple-jwt有默认的配置参数

rest_framework_simplejwt—settings.py

DEFAULTS = {# Access Token的有效期"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),# Refresh Token的有效期"REFRESH_TOKEN_LIFETIME": timedelta(days=1),# 是否自动刷新Refresh Token"ROTATE_REFRESH_TOKENS": False,# 刷新Refresh Token时是否将旧Token加入黑名单,如果设置为False,则旧的刷新令牌仍然可以用于获取新的访问令牌"BLACKLIST_AFTER_ROTATION": False,# 如为True,则在每次使用访问令牌进行身份验证时,更新用户最后登录时间"UPDATE_LAST_LOGIN": False,# 加密算法"ALGORITHM": "HS256",# 签名秘钥,这里使用Django的SECRET_KEY"SIGNING_KEY": settings.SECRET_KEY,# 用于验证jwt签名的秘钥返回的内容"VERIFYING_KEY": "",# JWT中的"Audience"声明,用于指定该JWT的预期接收者"AUDIENCE": None,# JWT中的"Issuer"声明,用于指定该JWT的发行者"ISSUER": None,# 用于序列化JWT负载的JSON编码器。默认为Django的JSON编码器"JSON_ENCODER": None,# 包含公钥的URL,用于验证JWT签名"JWK_URL": None,# 允许的时钟偏差量,以秒为单位"LEEWAY": 0,# 用于指定JWT在HTTP请求头中使用的身份验证方案。默认为"Bearer""AUTH_HEADER_TYPES": ("Bearer",),# 包含JWT的HTTP请求头的名称。默认为"HTTP_AUTHORIZATION""AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",# 用户模型中用作用户ID的字段。默认为"id""USER_ID_FIELD": "id",# JWT负载中包含用户ID的声明。默认为"user_id""USER_ID_CLAIM": "user_id",# 用于指定用户身份验证规则的函数或方法。默认使用Django的默认身份验证方法进行身份验证"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",# 用于指定可以使用的令牌类。默认为"rest_framework_simplejwt.tokens.AccessToken""AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),# JWT负载中包含令牌类型的声明。默认为"token_type""TOKEN_TYPE_CLAIM": "token_type",# JWT负载中包含JWT ID的声明。默认为"jti""JTI_CLAIM": "jti",# 用于指定可以使用的用户模型类"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",# 在使用滑动令牌时,JWT负载中包含刷新令牌过期时间的声明"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",# 滑动令牌的生命周期。默认为5分钟"SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),# 滑动令牌可以用于刷新的时间段。默认为1天"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),# 用于生成access和刷refresh的序列化器"TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",# 用于刷新访问令牌的序列化器"TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",# 用于验证令牌的序列化器"TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",# 用于列出或撤销已失效JWT的序列化器"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,# 在令牌中用于标识撤销令牌的声明"REVOKE_TOKEN_CLAIM": "hash_password",
}

定制登录返回格式

  • 自定义一个序列化类,重写validate方法
class CommonTokenObtainSerializer(TokenObtainPairSerializer):def validate(self, attrs):# 获取父类中的原数据dic = super().validate(attrs)data = {'code': 100,'msg': '登录成功','username': self.user.username,'refresh': dic.get('refresh'),'access': dic.get('access')}return data
  • 配置文件注册
# settings.py
SIMPLE_JWT = {'TOKEN_OBTAIN_SERIALIZER': 'user.userserializers.CommonTokenObtainSerializer'
}

image-20240419233002835

定制payload格式

class CommonTokenObtainSerializer(TokenObtainPairSerializer):@classmethoddef get_token(cls, user):# 获取父类得到的tokentoken = super().get_token(user)print(token.payload)token['name'] = user.usernamereturn tokendef validate(self, attrs):dic = super().validate(attrs)data = {'code': 100,'msg': '登录成功','username': self.user.username,'refresh': dic.get('refresh'),'access': dic.get('access')}return data
  • 获取payload属性得到{'token_type': 'refresh', 'exp': 1713627459, 'iat': 1713541059, 'jti': '8cf1b417155d4ca59965b6625b73831d', 'user_id': 2}
  • 因此可以直接token['name']为payload添加属性
  • 重写后jwt的第三段也会跟着改变

自定制签发-认证

  • 自己生成JWT
  • 用钩子函数将自己要签发的JWT放入序列化类
  • 将签发的JWT放入浏览器再被自定制的认证类接受
  • 校验JWT的合法性然后返回
# views.py
import uuid
from datetime import datetimefrom rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, GenericViewSet
from user import models
from user.MyAuthentication import JWTOurAuth
from user.MySerializers import UserSerializer
from rest_framework_simplejwt.authentication import JWTAuthenticationclass UserView(GenericViewSet):queryset = models.UserLog.objects.all()serializer_class = UserSerializerauthentication_classes = [JWTAuthentication]permission_classes = []	# 局部禁用权限校验@action(methods=['POST'], detail=False)def login(self, request):username = (request.data.get('username'))password = (request.data.get('password'))serializer = self.get_serializer(data=request.data)# 用户密码正确则签发,并发送给浏览器META中,顺带返回JWTif models.UserLog.objects.get(username=username).check_password(password):if serializer.is_valid():request.META['MY_TOKEN'] = serializer.validated_data['JWT']return Response({"msg": '登录成功', 'result': serializer.validated_data})else:return Response({"msg": '账号或密码错误'})class UserDetail(ModelViewSet):queryset = models.UserLog.objects.all()serializer_class = UserSerializerauthentication_classes = [JWTOurAuth]	# 使用自定义的认证类@action(methods=['GET'], detail=False)def findall(self, request):res = self.get_queryset()serializer = self.get_serializer(instance=res, many=True)return Response({'msg': '查询成功', 'result': serializer.data})
# serializer.py
from datetime import datetimefrom rest_framework import serializers
from .MyJWT import MyJWT
from .models import UserLogclass UserSerializer(serializers.Serializer):username = serializers.CharField()password = serializers.CharField()def validate(self, attrs):# 模拟JWT第二段userinfo = {'name': attrs["username"], 'time_field': str(datetime(2024, 1, 1, 12, 0)), 'time_expires': str(datetime(2024, 1, 2, 12, 0)), 'level': UserLog.objects.get(username=attrs["username"]).level}jwt_obj = MyJWT(payload=userinfo)	# 将用户数据放入自定制的JWT生成类中生成JWTJWT = jwt_obj.Issuance()attrs['JWT'] = JWT	# 将生成的JWT返回return attrs
# MyJWT.py 用于生成JWT格式字符串
from datetime import datetime
import json
import base64
import hashlibclass MyJWT:def __init__(self, payload=None, token=None):self.header = {'type': 'jwt', 'alg': 'md5'}self.payload = payloadself.token = tokendef get_base64(self, info):return base64.b64encode((json.dumps(info)).encode(encoding='utf-8'))def get_md5(self, header, payload):md5 = hashlib.md5()md5.update(header)md5.update(payload)md5.update(b'7777')  # 盐return md5.hexdigest()def get_token(self, header, payload):base64_header = self.get_base64(header)base64_payload = self.get_base64(payload)token = f'{base64_header.decode(encoding="utf8")}.{base64_payload.decode(encoding="utf8")}.{self.get_md5(base64_header, base64_payload)}'return tokendef check_token(self, token):base64_header, base64_payload, sign = token.split('.')token = self.get_md5(base64_header.encode('utf8'), base64_payload.encode('utf8'))if sign == token:return Trueelse:return Falsedef Issuance(self):token = self.get_token(self.header, self.payload)return tokendef Validate(self, token):if self.check_token(token):return Trueelse:return False@staticmethoddef get_user(Token):if 'name' not in  json.loads(base64.b64decode(Token.split('.')[1]).decode(encoding='utf8')):return Falsereturn json.loads(base64.b64decode(Token.split('.')[1]).decode(encoding='utf8'))['name']# 使用方法---放入数据(第二段)即可调用Issuance方法获取生成的JWT,调用Validate方法校验参数Token是否与原数据匹配
# userinfo = {'name': '张三', 'age': 18, 'time_field': str(datetime(2024, 1, 1, 12, 0)), 'time_expires': str(
#     datetime(2024, 1, 2, 12, 0)), 'level': 1}
# jwt = MyJWT(payload=userinfo)
# Token = jwt.Issuance()
# user = MyJWT.get_user(Token)
# Token_validate = jwt.Validate(Token)
# MyAuthentication.py
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.authentication import JWTAuthenticationfrom .MyJWT import MyJWTclass JWTOurAuth(JWTAuthentication):def authenticate(self, request):# 从浏览器获取tokenif 'HTTP_TOKEN' not in request.META:raise AuthenticationFailed("请添加token")jwt = request.META['HTTP_TOKEN']# 如果验证通过则直接返回用户与jwt字符串jwt_obj = MyJWT()JWT_validate = jwt_obj.Validate(jwt)if JWT_validate:return jwt_obj.get_user(jwt), jwtraise AuthenticationFailed('你的JWT有误')if 'HTTP_TOKEN' not in request.META:raise AuthenticationFailed("请添加token")jwt = request.META['HTTP_TOKEN']# 如果验证通过则直接返回用户与jwt字符串jwt_obj = MyJWT()JWT_validate = jwt_obj.Validate(jwt)if JWT_validate:return jwt_obj.get_user(jwt), jwtraise AuthenticationFailed('你的JWT有误')

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

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

相关文章

SAP 销售业务中免费货物的会计核算

此博文主要介绍SAP销售业务中免费货物解决方案中,免费货物的会计核算。如果需要进一步了解SAP SD 销售与分销业务中,免费货物的标准解决方案概览,可先了解本博客博文:SAP销售与分销中的免费货物解决方案相关文章: htt…

SQLite R*Tree 模块(三十三)

返回:SQLite—系列文章目录 上一篇:SQLite FTS3 和 FTS4 扩展(三十二) 下一篇:SQLite轻量级会话扩展(三十四) 1. 概述 R-Tree 是一个特殊的 专为执行范围查询而设计的索引。R-树最常见的是 用于地理空间系统,其中…

【Interconnection Networks 互连网络】Dragonfly Topology 蜻蜓网络拓扑

蜻蜓拓扑 Dragonfly Topology 1. 拓扑参数2. Topology Description 拓扑描述3. Topology Variations 拓扑变体 蜻蜓拓扑 Dragonfly Topology 1. 拓扑参数 Dragonfly拓扑参数: N N N: 网络中终端(terminal)的总数量 p p p: 连接到每个路由器的终端数量 a a a: 每…

算法|最大堆、最小堆和堆排序的实现(JavaScript)

一些概念 堆&#xff1a;特殊的完全二叉树&#xff0c;具有特定性质的完全二叉树。大根堆&#xff1a;父节点 > 子节点小根堆&#xff1a;父节点 < 子节点 二叉堆也属于完全二叉树&#xff0c;所以可以用数组表示。 若下标从1开始&#xff0c;左节点为 2*i &#xff0…

数字谐振器设计

数字谐振器设计 电路里的谐振:当电路中激励的频率等于电路的固有频率时&#xff0c;电路电磁振荡的振幅也将达到峰值。 形式一 形式二 例子

Python程序设计 字典

教学案例十 字典 1. 判断出生地 sfz.txt文件中存储了地区编码和地区名称 身份证的前6位为地区编码&#xff0c;可以在sfz.txt文件中查询到地区编号对应的地区名称 编写程序&#xff0c;输入身份证号&#xff0c;查询并显示对应的地区名称 若该地区编码不在文件中&#xff0c;…

文本生成任务的评价方法BLEU 和 ROUGE

BLEU 是 2002 年提出的&#xff0c;而 ROUGE 是 2003 年提出的。这两种指标虽然存在着一些问题&#xff0c;但是仍然是比较主流的评价指标。 BLUE BLEU 的全称是 Bilingual evaluation understudy&#xff0c;BLEU 的分数取值范围是 0&#xff5e;1&#xff0c;分数越接近1&a…

【JavaWeb】Day51.Mybatis动态SQL

什么是动态SQL 在页面原型中&#xff0c;列表上方的条件是动态的&#xff0c;是可以不传递的&#xff0c;也可以只传递其中的1个或者2个或者全部。 而在我们刚才编写的SQL语句中&#xff0c;我们会看到&#xff0c;我们将三个条件直接写死了。 如果页面只传递了参数姓名name 字…

把idea的Java代码中的包打开,以层级的方式显示

我们在使用idea敲谢Java代码的时候&#xff0c;会注意到包显示在一起&#xff0c;这样对于有一些开发来说节省了空间&#xff0c;但是对于一些程序员来说看起来不舒服&#xff0c;而且不好操作。 针对这样的情况&#xff0c;我们需要取消一项勾选。 点击这三个... 取消这个按钮…

LeetCode 热题 100 题解:普通数组部分

文章目录 题目一&#xff1a;最大子数组和&#xff08;No. 53&#xff09;题解 题目二&#xff1a;合并区间&#xff08;No. 56&#xff09;题解 题目三&#xff1a;轮转数组&#xff08;No. 189&#xff09;题解 题目四&#xff1a;除自身以外数组的乘积&#xff08;No. 238&a…

深入剖析Spring框架:循环依赖的解决机制

你好&#xff0c;我是柳岸花开。 什么是循环依赖&#xff1f; 很简单&#xff0c;就是A对象依赖了B对象&#xff0c;B对象依赖了A对象。 在Spring中&#xff0c;一个对象并不是简单new出来了&#xff0c;而是会经过一系列的Bean的生命周期&#xff0c;就是因为Bean的生命周期所…

【高阶数据结构】并查集 -- 详解

一、并查集的原理 1、并查集的本质和概念 &#xff08;1&#xff09;本质 并查集的本质&#xff1a;森林。 &#xff08;2&#xff09;概念 在一些应用问题中&#xff0c;需要将 n 个不同的元素划分成一些不相交的集合。 开始时&#xff0c;每个元素自成一个单元素集合&…