JWT自定义校验规则与生成、用户多种方式登陆、搜索过滤组件,排序组件,分页组件
# 自定义校验token规则
1.视图类
from .authentications import JWTAuthentication class UserDetail1(APIView): permission_classes = [IsAuthenticated] # 必须登录 authentication_classes = [JWTAuthentication] # jwt用户token自定义登陆认证规则 def get(self, request, *args, **kwargs): return APIResponse(results={'username': request.user.username})
2.自定义token规则,在api生成一个authentications.py认证文件
import jwt from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication from rest_framework_jwt.authentication import jwt_decode_handler from rest_framework.exceptions import AuthenticationFailed class JWTAuthentication(BaseJSONWebTokenAuthentication): def authenticate(self, request): jwt_token = request.META.get('HTTP_AUTHORIZATION') # 自定义规则 :auth token jwt,调用下面规则方法 token = self.parse_jwt_token(jwt_token) # 如果没有值,无法校验 if token is None: return None try: # token =>payload 反向解析出payload payload = jwt_decode_handler(token) except jwt.ExpiredSignature: # 判断是否过期 raise AuthenticationFailed('token已过期') except: raise AuthenticationFailed('非法用户') user = self.authenticate_credentials(payload) # 根据payload解析出user return (user, token) # 自定义校验规则:auth token jwt ,auth为前言,jwt为后缀 def parse_jwt_token(self, jw_token): tokens = jw_token.split() if len(tokens) != 3 or tokens[0].lower() != 'auth' or tokens[2].lower() != 'jwt': return None # 把token核心内容返回进行校验 return tokens[1]
# 自定义 drf-jwt 配置
import datetime JWT_AUTH = { # user => payload 'JWT_PAYLOAD_HANDLER': 'rest_framework_jwt.utils.jwt_payload_handler', # payload => token 'JWT_ENCODE_HANDLER': 'rest_framework_jwt.utils.jwt_encode_handler', # token => payload 'JWT_DECODE_HANDLER': 'rest_framework_jwt.utils.jwt_decode_handler', # token过期时间 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # token刷新的过期时间 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7), # 反爬小措施前缀 'JWT_AUTH_HEADER_PREFIX': 'JWT', }
实现多种方式登录签发token:比如 -账号、手机号、邮箱等登录:
1.禁用认证与权限组件
2.拿到前台登录信息,交给序列化类
3.序列化校验得到登陆用户与token存放在序列化对象中
4.取出登陆用户与token返回给前台
"""
from .serializers import UserModelSerializer
# 1.视图类
class LoginAPIView(APIView): authentication_classes = [] # 禁用认证 permission_classes = [] # 禁用权限 def post(self, request, *args, **kwargs): user_ser = UserModelSerializer(data=request.data) # 反序列化进行校验 # 校验通过,如果没有报异常 user_ser.is_valid(raise_exception=True) # 正常登陆,把生成的token返回给前端 return APIResponse(token=user_ser.token, results=UserModelSerializer(user_ser.user).data)
#2. 序列化类,进行校验,生成token发送出去
from rest_framework import serializers from . import models import re from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler class UserModelSerializer(serializers.ModelSerializer): # 自定义反序列化字段:一定要设置write_only,值参与反序列化,不会与model类字段映射 usr = serializers.CharField(write_only=True) pwd = serializers.CharField(write_only=True) class Meta: model = models.User fields = ['usr', 'pwd', 'username', 'mobile', 'email'] # 系统校验规则 extra_kwargs = { # 'usr': { # 'required': True, # 必须校验 # 'min_length': 3, # 'error_messages': { # 'required': '必须填写,你个铺盖', # 'min_length': '太短楼!', # } # }, 'username': { 'read_only': True }, 'mobile': { 'read_only': True }, 'email': { 'read_only': True } } # 全局钩子,attrs里面是通过校验的 def validate(self, attrs): usr = attrs.get('usr') pwd = attrs.get('pwd') # 多方式登录:各分支处理得到该方式对应的用户 if re.match(r'.+@.+', usr): user_query = models.User.objects.filter(email=usr) elif re.match(r'1[3-9][0-9]{9}', usr): user_query = models.User.objects.filter(mobile=usr) else: user_query = models.User.objects.filter(username=usr) user_obj = user_query.first() if user_obj and user_obj.check_password(pwd): # 签发生成token,将token存放到实例化对象的中 payload = jwt_payload_handler(user_obj) # 把头部,和载荷过期时间,user对象,生成payload token = jwt_encode_handler(payload) # 把头部,载荷,和秘=秘钥经过加密生成token self.token = token # 把token赋值到对象中 self.user = user_obj print(token) return attrs raise serializers.ValidationError({'data': '数据提供有误'})
搜索过滤,排序,分页:
from . import models from .serializers import CarModelSerializer # Car的群查接口 from rest_framework.generics import ListAPIView # 1.drf的SearchFilter - 搜索过滤 from rest_framework.filters import SearchFilter # 2.drf的OrderingFilter - 排序过滤 from rest_framework.filters import OrderingFilter # 3.drf的分页类 - 自定义 from . import pagenations class CarListAPIView(ListAPIView): permission_classes = [] # 权限取消 authentication_classes = [] # 认证取消 queryset = models.Car.objects.all() serializer_class = CarModelSerializer # 局部配置 过滤类 们(全局配置用DEFAULT_FILTER_BACKENDS) filter_backends = [SearchFilter,OrderingFilter] # SearchFilter过滤类依赖的过滤条件 => 接口:/cars/?search=... search_fields = ['name', 'price'] # OrderingFilter过滤类依赖的过滤条件 => 接口:/cars/?ordering=...,正是升序,-则是降序 ordering_fields = ['pk', 'price'] # eg:/cars/?ordering=-price,pk,先按price降序,如果出现price相同,再按pk升序 pagination_class = pagenations.MyPageNumberPagination
# 自定义分页类
from rest_framework.pagination import PageNumberPagination class MyPageNumberPagination(PageNumberPagination): # ?page=页码 page_query_param = 'page' # ?page=页面 下默认一页显示的条数 page_size = 3 # ?page=页面&page_size=条数 用户自定义一页显示的条数 page_size_query_param = 'page_size' max_page_size = 5