签发token

源码的入口:完成token签发的view类里面封装的方法。

源码中在请求token的时候只有post请求方法,主要分析一下源码中的post方法的实现。

settings源码:

总结:

源码的入口

# 前提:给一个局部禁用了所有 认证与权限 的视图类发送用户信息得到token,其实就是登录接口

# 1)rest_framework_jwt.views.ObtainJSONWebToken 的 父类 JSONWebTokenAPIView 的 post 方法
# 接受有username、password的post请求 # 2)post方法将请求数据交给 rest_framework_jwt.serializer.JSONWebTokenSerializer 处理
# 完成数据的校验,会走序列化类的 全局钩子校验规则,校验得到登录用户并签发token存储在序列化对象中

核心内部源码分析:

rest_framework_jwt.serializer.JSONWebTokenSerializer的validate(self, attrs)方法

实现签发token的内部方法

def validate(self, attrs):
# 账号密码字典
credentials = {
self.username_field: attrs.get(self.username_field),
'password': attrs.get('password')
}
if all(credentials.values()):
# 签发token第1步:用账号密码得到user对象
user = authenticate(**credentials)
if user:
if not user.is_active:
msg = _('User account is disabled.')
raise serializers.ValidationError(msg)
# 签发token第2步:通过user得到payload,payload包含着用户信息与过期时间
payload = jwt_payload_handler(user)
# 在视图类中,可以通过 序列化对象.object.get('user'或者'token') 拿到user和token
return {
# 签发token第3步:通过payload签发出token
'token': jwt_encode_handler(payload),
'user': user
}
else:
msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg)
else:
msg = _('Must include "{username_field}" and "password".')
msg = msg.format(username_field=self.username_field)
raise serializers.ValidationError(msg)

根据研究分析源码,实现手动签发token逻辑

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

校验token

源码入口

# 前提:访问一个配置了jwt认证规则的视图类,就需要提交认证字符串token,在认证类中完成token的校验

# 1)rest_framework_jwt.authentication.JSONWebTokenAuthentication 的 父类 BaseJSONWebTokenAuthentication 的 authenticate 方法
# 请求头拿认证信息jwt-token => 通过反爬小规则确定有用的token => payload => user

核心源码:

rest_framework_jwt.authentication.BaseJSONWebTokenAuthentication的authenticate(self, request)方法
def authenticate(self, request):
"""
Returns a two-tuple of `User` and token if a valid signature has been
supplied using JWT-based authentication. Otherwise returns `None`.
"""
# 带有反爬小规则的获取token:前台必须按 "jwt token字符串" 方式提交
# 校验user第1步:从请求头 HTTP_AUTHORIZATION 中拿token,并提取
jwt_value = self.get_jwt_value(request)
# 游客
if jwt_value is None:
return None
# 校验
try:
# 校验user第2步:token => payload
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
msg = _('Signature has expired.')
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = _('Error decoding signature.')
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
# 校验user第3步:token => payload
user = self.authenticate_credentials(payload) return (user, jwt_value)

手动校验token逻辑

# 1)从请求头中获取token
# 2)根据token解析出payload:jwt_decode_handler(token) => payloay
# from rest_framework_jwt.authentication import jwt_decode_handler
# 3)根据payload解析出user:self.authenticate_credentials(payload) => user
# 继承drf-jwt的BaseJSONWebTokenAuthentication,拿到父级的authenticate_credentials方法

案例:实现多方式登录签发token

models.py

from django.db import models

from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
mobile = models.CharField(max_length=11, unique=True) class Meta:
db_table = 'api_user'
verbose_name = '用户表'
verbose_name_plural = verbose_name def __str__(self):
return self.username

serializers.py

from rest_framework import serializers
from . import models
import re # 拿到前台token的两个函数: user => payload => token
# from rest_framework_jwt.settings import api_settings
# jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
# jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
from rest_framework_jwt.serializers import jwt_payload_handler
from rest_framework_jwt.serializers import jwt_encode_handler # 1) 前台提交多种登录信息都采用一个key,所以后台可以自定义反序列化字段进行对应
# 2) 序列化类要处理序列化与反序列化,要在fields中设置model绑定的Model类所有使用到的字段
# 3) 区分序列化字段与反序列化字段 read_only | write_only
# 4) 在自定义校验规则中(局部钩子、全局钩子)校验数据是否合法、确定登录的用户、根据用户签发token
# 5) 将登录的用户与签发的token保存在序列化类对象中
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 = {
'username': {
'read_only': True
},
'mobile': {
'read_only': True
},
'email': {
'read_only': True
},
} 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() # 签发:得到登录用户,签发token并存储在实例化对象中
if user_obj and user_obj.check_password(pwd):
# 签发token,将token存放到 实例化类对象的token 名字中
payload = jwt_payload_handler(user_obj)
token = jwt_encode_handler(payload)
# 将当前用户与签发的token都保存在序列化对象中
self.user = user_obj
self.token = token
return attrs raise serializers.ValidationError({'data': '数据有误'})

views.py

#实现多方式登陆签发token:账号、手机号、邮箱等登陆
# 1) 禁用认证与权限组件
# 2) 拿到前台登录信息,交给序列化类
# 3) 序列化类校验得到登录用户与token存放在序列化对象中
# 4) 取出登录用户与token返回给前台 import re
from . import serializers, models
from utils.response import APIResponse from rest_framework_jwt.serializers import jwt_payload_handler
from rest_framework_jwt.serializers import jwt_encode_handler class LoginAPIView(APIView):
# 1) 禁用认证与权限组件
authentication_classes = []
permission_classes = []
def post(self, request, *args, **kwargs):
# 2) 拿到前台登录信息,交给序列化类,规则:账号用usr传,密码用pwd传
user_ser = serializers.UserModelSerializer(data=request.data)
# 3) 序列化类校验得到登录用户与token存放在序列化对象中
user_ser.is_valid(raise_exception=True)
# 4) 取出登录用户与token返回给前台
return APIResponse(token=user_ser.token, results=serializers.UserModelSerializer(user_ser.user).data) # "一根筋" 思考方式:所有逻辑都在视图类中处理
def my_post(self, request, *args, **kwargs):
usr = request.data.get('usr')
pwd = request.data.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):
payload = jwt_payload_handler(user_obj)
token = jwt_encode_handler(payload)
return APIResponse(results={'username': user_obj.username}, token=token)
return APIResponse(data_msg='不可控错误')

案例:自定义认证反爬规则的认证类

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 = jwt_decode_handler(token)
except jwt.ExpiredSignature:
raise AuthenticationFailed('token已过期')
except:
raise AuthenticationFailed('非法用户')
# payload => user
user = self.authenticate_credentials(payload) return (user, token) # 自定义校验规则:auth token jwt,auth为前盐,jwt为后盐
def parse_jwt_token(self, jwt_token):
tokens = jwt_token.split()
if len(tokens) != 3 or tokens[0].lower() != 'auth' or tokens[2].lower() != 'jwt':
return None
return tokens[1]

views.py

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

admin使用自定义User表:新增用户密码和密文

from django.contrib import admin
from . import models # 自定义User表,admin后台管理,采用密文密码
from django.contrib.auth.admin import UserAdmin class MyUserAdmin(UserAdmin):
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'password1', 'password2', 'mobile', 'email'),
}),
) admin.site.register(models.User, MyUserAdmin)

群查接口各种筛选组件数据准备

models.py

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

admin.py

admin.site.register(models.Car)

serializers.py

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

viers.py

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

注意点:

urls.py

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

drf搜索过滤组件

views.py

from rest_framework.generics import ListAPIView

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

drf排序过滤组件

views.py ,与上面的过滤组件区别:导入的是 OrderingFilter

from rest_framework.generics import ListAPIView

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

drf基础分页组件

pahenations.py  通过源码自定义的

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'
# 用户自定义一页显示的条数最大限制:数值超过5也只显示5条
max_page_size = 5

views.py

from rest_framework.generics import ListAPIView

class CarListAPIView(ListAPIView):
# 如果queryset没有过滤条件,就必须 .all(),不然分页会出问题
queryset = models.Car.objects.all()
serializer_class = serializers.CarModelSerializer # 分页组件 - 给视图类配置分页类即可 - 分页类需要自定义,继承drf提供的分页类即可
pagination_class = pagenations.MyPageNumberPagination

源码内部分析,实现自定义的分页组件功能,一般都是参考源码的封装方法,来进一步书写自定义的。

以上几种筛选(过滤、排序、分页)组件,区别在于访问的url根据自定一的规则不同,方法也不同

drf_jwt手动签发与校验-drf小组件:过滤-筛选-排序-分页的更多相关文章

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

    复习 """ 频率组件:限制接口的访问频率 源码分析:初始化方法.判断是否有权限方法.计数等待时间方法 自定义频率组件: class MyThrottle(SimpleR ...

  2. DRF之Jwt 实现自定义和DRF小组件及django-filter插件的使用

    一.DRF之Jwt 实现自定义 二.DRF(过滤,排序,分页)组件 三.Django-filter插件的使用和自定义 """ 1.drf-jwt手动签发与校验 :只是做t ...

  3. elment-ui table组件 -- 远程筛选排序

    elment-ui table组件 -- 远程筛选排序 基于 elment-ui table组件 开发,主要请求后台实现筛选 排序的功能. 需求 排序 筛选 是对后台整个数据进行操作,而不是对当前页面 ...

  4. DRF框架(八)——drf-jwt手动签发与校验、搜索过滤组件、排序过滤组件、基础分页组件

    自定义drf-jwt手动签发和校验 签发token源码入口 前提:给一个局部禁用了所有 认证与权限 的视图类发送用户信息得到token,其实就是登录接口,不然进不了登录页面 获取提交的username ...

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

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

  6. drf中的jwt使用与手动签发效验

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

  7. 9) drf JWT 认证 签发与校验token 多方式登陆 自定义认证规则反爬 admin密文显示

    一 .认证方法比较 1.认证规则图 django 前后端不分离 csrf认证 drf 前后端分离 禁用csrf 2. 认证规则演变图 数据库session认证:低效 缓存认证:高效 jwt认证:高效 ...

  8. drf认证组件、权限组件、jwt认证、签发、jwt框架使用

    目录 一.注册接口 urls.py views.py serializers.py 二.登录接口 三.用户中心接口(权限校验) urls.py views.py serializers.py 四.图书 ...

  9. drf认证组件(介绍)、权限组件(介绍)、jwt认证、签发、jwt框架使用

    目录 一.注册接口 urls.py views.py serializers.py 二.登录接口 三.用户中心接口(权限校验) urls.py views.py serializers.py 四.图书 ...

随机推荐

  1. 14 SQLite数据库

    SQLite数据库SQLite 是一款轻型的数据库SQLite 的设计目标是嵌入式的SQLite 占用资源低SQL 指结构化查询语言SQL 使我们有能力访问数据库SQL 是一种 ANSI 的标准计算机 ...

  2. Elasticsearch 批处理

    章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...

  3. 说说lock到底要锁谁?

    波安搬... http://www.cnblogs.com/wolf-sun/p/4209521.html ---------------------------------------------- ...

  4. Nmap目录扫描和漏洞扫描(9.27 第十三天)

    目录扫描:扫描站点的目录,寻找敏感文件(目录名.探针文件.后台.robots.txt[].备份文件等) 目录:站点结构,权限控制不严格 探针文件:服务器配置信息,phpinfo.php   readm ...

  5. CF_448D 二分

    给定n m k n和m为一个矩阵的行和列,都从1开始,矩阵的每个元素的值即为 i*j(行*列),求里面第k个数 还想找什么规律,发现虽然矩阵里面很有规律,但是n 和m在不断变化 根本不好找 其实元素从 ...

  6. 第四章:Schema与数据类型优化

    1. 选择优化的数据类型 选择数据类型的原则 更小的通常更好:选择可以正确存储数据的最小数据类型 小的数据类型消耗更少的内存.CPU;占用更少的磁盘 选用简单的数据类型:简单的数据类型通常需要更少的C ...

  7. [GWCTF 2019]枯燥的抽奖

    0x00 知识点 种子爆破 工具 http://www.openwall.com/php_mt_seed 0x01 解题 查看源码进入check.php zOHF0Cxp49 <?php #这不 ...

  8. Teamviewer 退出时设置不锁屏

    在pc端teamviewer登录同伴的机器时,页面最上端会出现一个工具栏,如图: 点击"动作"  ---〉"锁定" 取消打钩 "在会话结束时锁定&qu ...

  9. SQL Server 2017 安装问题

    遇到问题 Polybase 要求安装 Oracle JRE7 更新 规则失败 安装完毕之后,登录提示:您试图连接的 SQL Server 实例未安装 安装完SQL Server 2017 后,无法启动 ...

  10. re模块2

    # 元字符+,*遇到?后就会变为贪婪匹配 print(re.findall('abc+?','abcccccc')) #['abc'] print(re.findall('abc*?','abcccc ...