一。签发token的原理

  当认证类authentication_classes是JSONWebTokenAuthentication时,其父类JSONWebTokenAPIView只有post 方法,所以需要用post提交。接受有username、password的post请求。

  post方法将请求数据交给 rest_framework_jwt.serializer.JSONWebTokenSerializer 处理。

  JSONWebTokenSerializer 中的初始化获取username和password进行反序列化,交给全局钩子进行处理。

  1. def validate(self, attrs):
  2. # 账号密码字典
  3. credentials = {
  4. self.username_field: attrs.get(self.username_field),
  5. 'password': attrs.get('password')
  6. }
  7. if all(credentials.values()):
  8. # 签发token第1步:用账号密码得到user对象
  9. user = authenticate(**credentials)
  10. if user:
  11. if not user.is_active:
  12. msg = _('User account is disabled.')
  13. raise serializers.ValidationError(msg)
  14. # 签发token第2步:通过user得到payload,payload包含着用户信息与过期时间
  15. payload = jwt_payload_handler(user)
  16. # 在视图类中,可以通过 序列化对象.object.get('user'或者'token') 拿到user和token
  17. return {
  18. # 签发token第3步:通过payload签发出token
  19. 'token': jwt_encode_handler(payload),
  20. 'user': user
  21. }
  22. else:
  23. msg = _('Unable to log in with provided credentials.')
  24. raise serializers.ValidationError(msg)
  25. else:
  26. msg = _('Must include "{username_field}" and "password".')
  27. msg = msg.format(username_field=self.username_field)
  28. raise serializers.ValidationError(msg)

  手动签发token原理:

  1. # 1)通过username、password得到user对象
  2. # 2)通过user对象生成payload:jwt_payload_handler(user) => payload
  3. # from rest_framework_jwt.serializers import jwt_payload_handler
  4. # 3)通过payload签发token:jwt_encode_handler(payload) => token
  5. # from rest_framework_jwt.serializers import jwt_encode_handler

二。自定义drf-jwt。

  在jwt中,会通过setting中的字符串获取配置,可以在setting中设置。

  1. # 自定义 drf-jwt 配置
  2. import datetime
  3. JWT_AUTH = {
  4. # user => payload
  5. 'JWT_PAYLOAD_HANDLER':
  6. 'rest_framework_jwt.utils.jwt_payload_handler',
  7. # payload => token
  8. 'JWT_ENCODE_HANDLER':
  9. 'rest_framework_jwt.utils.jwt_encode_handler',
  10. # token => payload
  11. 'JWT_DECODE_HANDLER':
  12. 'rest_framework_jwt.utils.jwt_decode_handler',
  13. # token过期时间
  14. 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
  15. # token刷新的过期时间
  16. 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
  17. # 反爬小措施前缀
  18. 'JWT_AUTH_HEADER_PREFIX': 'JWT',
  19. }

三,实现多方式登陆签发token

  也就是要重写view,和其序列化组件:

  1. from django.db import models
  2.  
  3. from django.contrib.auth.models import AbstractUser
  4. class User(AbstractUser):
  5. mobile = models.CharField(max_length=11, unique=True)
  6.  
  7. class Meta:
  8. db_table = 'api_user'
  9. verbose_name = '用户表'
  10. verbose_name_plural = verbose_name
  11.  
  12. def __str__(self):
  13. return self.username

models.py

  1. from rest_framework import serializers
  2. from . import models
  3. import re
  4.  
  5. # 拿到前台token的两个函数: user => payload => token
  6. # from rest_framework_jwt.settings import api_settings
  7. # jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
  8. # jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
  9. from rest_framework_jwt.serializers import jwt_payload_handler
  10. from rest_framework_jwt.serializers import jwt_encode_handler
  11.  
  12. # 1) 前台提交多种登录信息都采用一个key,所以后台可以自定义反序列化字段进行对应
  13. # 2) 序列化类要处理序列化与反序列化,要在fields中设置model绑定的Model类所有使用到的字段
  14. # 3) 区分序列化字段与反序列化字段 read_only | write_only
  15. # 4) 在自定义校验规则中(局部钩子、全局钩子)校验数据是否合法、确定登录的用户、根据用户签发token
  16. # 5) 将登录的用户与签发的token保存在序列化类对象中
  17. class UserModelSerializer(serializers.ModelSerializer):
  18. # 自定义反序列字段:一定要设置write_only,只参与反序列化,不会与model类字段映射
  19. usr = serializers.CharField(write_only=True)
  20. pwd = serializers.CharField(write_only=True)
  21. class Meta:
  22. model = models.User
  23. fields = ['usr', 'pwd', 'username', 'mobile', 'email']
  24. # 系统校验规则
  25. extra_kwargs = {
  26. 'username': {
  27. 'read_only': True
  28. },
  29. 'mobile': {
  30. 'read_only': True
  31. },
  32. 'email': {
  33. 'read_only': True
  34. },
  35. }
  36.  
  37. def validate(self, attrs):
  38. usr = attrs.get('usr')
  39. pwd = attrs.get('pwd')
  40.  
  41. # 多方式登录:各分支处理得到该方式下对应的用户
  42. if re.match(r'.+@.+', usr):
  43. user_query = models.User.objects.filter(email=usr)
  44. elif re.match(r'1[3-9][0-9]{9}', usr):
  45. user_query = models.User.objects.filter(mobile=usr)
  46. else:
  47. user_query = models.User.objects.filter(username=usr)
  48. user_obj = user_query.first()
  49.  
  50. # 签发:得到登录用户,签发token并存储在实例化对象中
  51. if user_obj and user_obj.check_password(pwd):
  52. # 签发token,将token存放到 实例化类对象的token 名字中
  53. payload = jwt_payload_handler(user_obj)
  54. token = jwt_encode_handler(payload)
  55. # 将当前用户与签发的token都保存在序列化对象中
  56. self.user = user_obj
  57. self.token = token
  58. return attrs
  59.  
  60. raise serializers.ValidationError({'data': '数据有误'})

serializers.py

  1. #实现多方式登陆签发token:账号、手机号、邮箱等登陆
  2. # 1) 禁用认证与权限组件
  3. # 2) 拿到前台登录信息,交给序列化类
  4. # 3) 序列化类校验得到登录用户与token存放在序列化对象中
  5. # 4) 取出登录用户与token返回给前台
  6. import re
  7. from . import serializers, models
  8. from utils.response import APIResponse
  9.  
  10. from rest_framework_jwt.serializers import jwt_payload_handler
  11. from rest_framework_jwt.serializers import jwt_encode_handler
  12.  
  13. class LoginAPIView(APIView):
  14. # 1) 禁用认证与权限组件
  15. authentication_classes = []
  16. permission_classes = []
  17. def post(self, request, *args, **kwargs):
  18. # 2) 拿到前台登录信息,交给序列化类,规则:账号用usr传,密码用pwd传
  19. user_ser = serializers.UserModelSerializer(data=request.data)
  20. # 3) 序列化类校验得到登录用户与token存放在序列化对象中
  21. user_ser.is_valid(raise_exception=True)
  22. # 4) 取出登录用户与token返回给前台
  23. return APIResponse(token=user_ser.token, results=serializers.UserModelSerializer(user_ser.user).data)
  24.  
  25. # "一根筋" 思考方式:所有逻辑都在视图类中处理
  26. def my_post(self, request, *args, **kwargs):
  27. usr = request.data.get('usr')
  28. pwd = request.data.get('pwd')
  29. if re.match(r'.+@.+', usr):
  30. user_query = models.User.objects.filter(email=usr)
  31. elif re.match(r'1[3-9][0-9]{9}', usr):
  32. user_query = models.User.objects.filter(mobile=usr)
  33. else:
  34. user_query = models.User.objects.filter(username=usr)
  35. user_obj = user_query.first()
  36. if user_obj and user_obj.check_password(pwd):
  37. payload = jwt_payload_handler(user_obj)
  38. token = jwt_encode_handler(payload)
  39. return APIResponse(results={'username': user_obj.username}, token=token)
  40. return APIResponse(data_msg='不可控错误')

views.py

  发送:

  1. {
  2. "usr":"",
  3. "pwd":""
  4. }

  接受:

  1. {
  2. "token":""
  3. }

四。自定义jwt认证规则。

  改写auth_class中的类。

  1. import jwt
  2. from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
  3. from rest_framework_jwt.authentication import jwt_decode_handler
  4. from rest_framework.exceptions import AuthenticationFailed
  5. class JWTAuthentication(BaseJSONWebTokenAuthentication):
  6. def authenticate(self, request):
  7. jwt_token = request.META.get('HTTP_AUTHORIZATION')
  8.  
  9. # 自定义校验规则:auth token jwt
  10. token = self.parse_jwt_token(jwt_token)
  11.  
  12. if token is None:
  13. return None
  14.  
  15. try:
  16. # token => payload
  17. payload = jwt_decode_handler(token)
  18. except jwt.ExpiredSignature:
  19. raise AuthenticationFailed('token已过期')
  20. except:
  21. raise AuthenticationFailed('非法用户')
  22. # payload => user
  23. user = self.authenticate_credentials(payload)
  24.  
  25. return (user, token)
  26.  
  27. # 自定义校验规则:auth token jwt,auth为前盐,jwt为后盐
  28. def parse_jwt_token(self, jwt_token):
  29. tokens = jwt_token.split()
  30. if len(tokens) != 3 or tokens[0].lower() != 'auth' or tokens[2].lower() != 'jwt':
  31. return None
  32. return tokens[1]

authentications.py

  1. from rest_framework.views import APIView
  2. from utils.response import APIResponse
  3. # 必须登录后才能访问 - 通过了认证权限组件
  4. from rest_framework.permissions import IsAuthenticated
  5. # 自定义jwt校验规则
  6. from .authentications import JWTAuthentication
  7. class UserDetail(APIView):
  8. authentication_classes = [JWTAuthentication]
  9. permission_classes = [IsAuthenticated]
  10. def get(self, request, *args, **kwargs):
  11. return APIResponse(results={'username': request.user.username})

views.py

五。自定义admin表

  可以通过useradmin的修改,继承,改变admin管理后台的方式:

  1. from django.contrib import admin
  2. from . import models
  3.  
  4. # 自定义User表,admin后台管理,采用密文密码
  5. from django.contrib.auth.admin import UserAdmin
  6.  
  7. class MyUserAdmin(UserAdmin):
  8. add_fieldsets = (
  9. (None, {
  10. 'classes': ('wide',),
  11. 'fields': ('username', 'password1', 'password2', 'mobile', 'email'),
  12. }),
  13. )
  14.  
  15. admin.site.register(models.User, MyUserAdmin)

六,数据准备:

  1. class Car(models.Model):
  2. name = models.CharField(max_length=16, unique=True, verbose_name='车名')
  3. price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='价格')
  4. brand = models.CharField(max_length=16, verbose_name='品牌')
  5.  
  6. class Meta:
  7. db_table = 'api_car'
  8. verbose_name = '汽车表'
  9. verbose_name_plural = verbose_name
  10.  
  11. def __str__(self):
  12. return self.name

models.py

  1. admin.site.register(models.Car)

admin.py

  1. class CarModelSerializer(serializers.ModelSerializer):
  2. class Meta:
  3. model = models.Car
  4. fields = ['name', 'price', 'brand']

serializers.py

  1. # Car的群查接口
  2. from rest_framework.generics import ListAPIView
  3.  
  4. class CarListAPIView(ListAPIView):
  5. queryset = models.Car.objects.all()
  6. serializer_class = serializers.CarModelSerializer

views.py

  1. url(r'^cars/$', views.CarListAPIView.as_view()),

urls.py

1。drf搜索过滤组件。

  在搜索过滤组件中,需要先调用一个类SearchFilter。

  当使用多数据返回的ListAPIView,返回多个数据时。设置了序列化之后。

  通过filter_backends设置刚刚导入的类。

  在search_fields中设置你需要搜索的字段。

  在get查询方法时就会过滤该条件的数据返回。

  源码中主要时需要从geneerics中的filter_queryset方法获取所有过滤器。

  1. from rest_framework.generics import ListAPIView
  2.  
  3. # 第一步:drf的SearchFilter - 搜索过滤
  4. from rest_framework.filters import SearchFilter
  5.  
  6. class CarListAPIView(ListAPIView):
  7. queryset = models.Car.objects.all()
  8. serializer_class = serializers.CarModelSerializer
  9.  
  10. # 第二步:局部配置 过滤类 们(全局配置用DEFAULT_FILTER_BACKENDS)
  11. filter_backends = [SearchFilter]
  12.  
  13. # 第三步:SearchFilter过滤类依赖的过滤条件 => 接口:/cars/?search=...
  14. search_fields = ['name', 'price']
  15. # eg:/cars/?search=1,name和price中包含1的数据都会被查询出

2。drf排序过滤组件

  排序组件和搜索组件擦不多。

  当get方法输入的时正pk时,会正序,如果书-pk,则会倒叙。

  执行过滤器中的filter_queryset方法

  1. from rest_framework.generics import ListAPIView
  2.  
  3. # 第一步:drf的OrderingFilter - 排序过滤
  4. from rest_framework.filters import OrderingFilter
  5.  
  6. class CarListAPIView(ListAPIView):
  7. queryset = models.Car.objects.all()
  8. serializer_class = serializers.CarModelSerializer
  9.  
  10. # 第二步:局部配置 过滤类 们(全局配置用DEFAULT_FILTER_BACKENDS)
  11. filter_backends = [OrderingFilter]
  12.  
  13. # 第三步:OrderingFilter过滤类依赖的过滤条件 => 接口:/cars/?ordering=...
  14. ordering_fields = ['pk', 'price']
  15. # eg:/cars/?ordering=-price,pk,先按price降序,如果出现price相同,再按pk升序

  多个过滤器之间使用分隔符&进行分割

3。drf基础分页组件

  在分页中,从list视图类中寻找page属性,再从generics中获取paginate_queryset,这个类只能有一个。

  所以调用pagination中的基础分页器进行改写:

  1. from rest_framework.pagination import PageNumberPagination
  2.  
  3. class MyPageNumberPagination(PageNumberPagination):
  4. # ?page=页码
  5. page_query_param = 'page'
  6. # ?page=页面 下默认一页显示的条数
  7. page_size = 3
  8. # ?page=页面&page_size=条数 用户自定义一页显示的条数
  9. page_size_query_param = 'page_size'
  10. # 用户自定义一页显示的条数最大限制:数值超过5也只显示5条
  11. max_page_size = 5

  view

  1. from rest_framework.generics import ListAPIView
  2.  
  3. class CarListAPIView(ListAPIView):
  4. # 如果queryset没有过滤条件,就必须 .all(),不然分页会出问题
  5. queryset = models.Car.objects.all()
  6. serializer_class = serializers.CarModelSerializer
  7.  
  8. # 分页组件 - 给视图类配置分页类即可 - 分页类需要自定义,继承drf提供的分页类即可
  9. pagination_class = pagenations.MyPageNumberPagination
  10. # 输入127.0.0.1/cars/?page=页码&page_size

day76_10_23自定义签发token,其他drf组件的更多相关文章

  1. drf_jwt手动签发与校验-drf小组件:过滤-筛选-排序-分页

    签发token 源码的入口:完成token签发的view类里面封装的方法. 源码中在请求token的时候只有post请求方法,主要分析一下源码中的post方法的实现. settings源码: 总结: ...

  2. drf中的jwt使用与手动签发token、校验用户

    jwt认证 1)session存储token,需要数据库参与,耗服务器资源.低效2)缓存存token,需要缓存参与,高效,不易集群3)客户端存token,服务器存签发与交易token的算法,高效,易集 ...

  3. 自定义user表签发token、自定义认证类、simpleui模块使用

    今日内容概要 自定义User表,签发token 自定义认证类 simpleui的使用 多方式登陆接口(后面也写 内容详细 1.自定义User表,签发token # 如果项目中的User表使用auth的 ...

  4. drf组件之jwt认证

    drf组件之jwt认证模块 一.认证规则 全称:json web token 解释:加密字符串的原始数据是json,后台产生,通过web传输给前台存储 格式:三段式 - 头.载荷.签名 - 头和载荷才 ...

  5. JWT签发token

    目录 一. 认证的发展历程简介 二. JWT签发Token源码分析 2.1 JWT工作原理及简介 2.2 JWT生成token源码分析 返回目录 一. 认证的发展历程简介 这里真的很简单的提一下认证的 ...

  6. day84-仿照admin实现一个自定义的增删改查组件

    一.admin的使用 app01的admin.py文件: class BookConfig(admin.ModelAdmin): list_display=[] list_display_links= ...

  7. 自定义admin管理工具(stark组件)

    自定义admin管理工具(stark组件) 创建项目 了解了admin的功能后,我们可以开始仿照admin编写我们自己的管理工具stark组件 首先创建一个新的项目,并创建三个app stark就是我 ...

  8. DRF 组件

    DRF组件中的认证  授权  频率限制   分页  注册器  url控件

  9. uniapp自定义picker城市多级联动组件

    uniapp自定义picker城市多级联动组件 支持多端--h5.app.微信小程序.支付宝小程序... 支持自定义配置picker插件级数 支持无限级 注意事项:插件传入数据格式为children树 ...

随机推荐

  1. 一、itk在VS2019上面的安装 和例子(HelloWorld)运行

    一.Itk简介 vtk是专门用于医疗图像处理的函数库,类似opencv. 这篇博客主要是讲解安装vtk之后的例子的运行,即如何构建自己的第一个ITK例子 二.Itk安装 Itk安装参考这篇博客: ht ...

  2. JS---DOM---为元素解除绑定事件

    解除绑定事件: 1.解绑事件 对象 .on 事件名字=事件处理函数--->绑定事件. 对象 .on 事件名字 = null . 注意:用什么方式绑定事件,就应该用对应的方式解除绑定事件. //1 ...

  3. acwing 652. 切蛋糕

    题目地址 今天是小Z的生日,同学们为他带来了一块蛋糕. 这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值. 小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大,但小Z ...

  4. 第05组 Beta冲刺(1/4)

    第05组 Beta冲刺(1/4) 队名:天码行空 组长博客连接 作业博客连接 团队燃尽图(共享): GitHub当日代码/文档签入记录展示(共享): 组员情况: 组员1:卢欢(组长) 过去两天完成了哪 ...

  5. Java入门之人需要注意的5大步骤

    作为最抢手的程序开发言语之一,Java在互联网领域中的方位无需赘言.抢手也带来了高薪和许多的作业时机,对那些预备通过学习Java来改动自己命运的同学来说,需求做好以下作业. 1.考虑一下 学习Java ...

  6. 解决SQL Server中无管理员账户权限问题

    遇到忘记SQL Server管理员账户密码或管理员账户被意外删除的情况,如何在SQL Server中添加一个新的管理员账户?按一下步骤操作可添加一个windows账户到SQL Server中,并分配数 ...

  7. matlab中的colormap

    matlab colormaps 默认颜色图是 parula ,颜色图从左往右数值不断增大. 颜色图名称 色阶 parula jet hsv hot cool spring summer autumn ...

  8. linux与ubuntu下vsftp的安装使用

    vsftp工具是linux与类linux系统上常用的ftp传输工具,按百度上的说法,它的不同点与好处有九点,不明觉厉,有兴趣的可以深入验证: 一.它是一个安全.高速.稳定的FTP服务器: 二.它可以做 ...

  9. 手动创建分区以及软硬raid的问题

    本文章旨在介绍安装centos7时手动分区问题,及其所对应的软硬raid问题.在新建centos7系统时,需要手动分区时,提供给我们三个选项: 在上图中,默认选项为“Standard Partitio ...

  10. ImportError: unable to find Qt5Core.dll on PATH

    一.实验环境 1.Windows7x32_SP1 2.python3.7.4 3.pyinstaller3.5 二.问题描述 1.一直都是在Windows10x64上使用pyinstaller打包ex ...