DRF---三种分页方式,jwt介绍和原理,django快速使用jwt,自定义用户表签发和认证token
三种分页方式
1) PageNumberPagination---基本分页
前端访问网址形式:
GET http://127.0.0.1:8000/students/?page=4
可以在子类中定义的属性:
- page_size 每页数目
- page_query_param 前端发送的页数关键字名,默认为”page”
- page_size_query_param 前端发送的每页数目关键字名,默认为None
- max_page_size 前端最多能设置的每页数量
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
class BookPagination(PageNumberPagination):
page_size = 2 # 默认每页展示的条数
page_query_param = 'page' # 查询页码的参数
page_size_query_param = 'size' # 控制每页显示的条数 ?size=3
max_page_size = 5 # 控制每页最多显示多少条数
2) LimitOffsetPagination---偏移分页
前端访问网址形式:
GET http://127.0.0.1/four/students/?limit=100&offset=400
可以在子类中定义的属性:
- default_limit 默认限制,默认值与
PAGE_SIZE设置一直 - limit_query_param limit参数名,默认’limit’
- offset_query_param offset参数名,默认’offset’
- max_limit 最大limit限制,默认None
class CommonLimitOffsetPagination(LimitOffsetPagination):
default_limit = 2 # 每页显示多少条
limit_query_param = 'limit' # 取多少条
offset_query_param = 'offset' #从第0个位置偏移多少开始取数据
max_limit = 5 # 最大限制条数
3) CursorPagination---游标分页
前端访问网址形式:
GET http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D
可以在子类中定义的属性:
- cursor_query_param:默认查询字段,不需要修改
- page_size:每页数目
- ordering:按什么排序,需要指定
class CommonCursorPagination(CursorPagination):
cursor_query_param = 'cursor' # 查询的名字 等同于 page=xx
page_size = 3 # 每页显示多少条
ordering = 'id' # 排序规则,必须是表中有的字段,一般用id
4) 继承APIView实现分页
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
class BookView(ViewSet):
def list(self,request):
books=Book.objects.all()
# 分页
# paginator=PageNumberPagination()
paginator=CommonLimitOffsetPagination() # 实例化自己写的分页类
#分页过后的数据
qs=paginator.paginate_queryset(books,request,self)
#序列化
ser=BookSerializer(qs,many=True)
# 第一种方式:每页总条数,上一页,下一页
# return Response(ser.data)
# 第二种:自己凑
# return Response({
# 'count':books.count(),
# 'next': paginator.get_next_link(),
# 'previous':paginator.get_previous_link(),
# 'results': ser.data
# })
# 第三种;直接使用分页类的方法
return paginator.get_paginated_response(ser.data)
jwt介绍和原理
1) 介绍
jwt:Json web token
彻底理解cookie,session,token:彻底理解cookie,session,token - 墨颜丶 - 博客园 (cnblogs.com)
cookie是存放在客户端浏览器的键值对
sessionId:asdfasdf----》放到cookie中
session是存放在服务端的键值对
asdfasdf:{id:3,name:lqz}
dddddd:{id:4,name:pyy}
token: 字符串分三段
-第一段:头(header)
jwt的头部承载两部分信息:
- 声明类型,这里是jwt
- 声明加密的算法 通常直接使用 HMAC SHA256
完整的头部就像下面这样的JSON:
{
'typ': 'JWT',
'alg': 'HS256'
}
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.
-第二段:载荷(payload)
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
- 标准中注册的声明
- 公共的声明
- 私有的声明
一般可以用来存放用户信息:
{
"id":3,
"name":"lqz",
"age":17,
}
然后将其进行base64加密,得到JWT的第二部分。
-第三段:签名(signature)
JWT的第三部分是一个签证信息,这个签证信息第一段和第二段通过某种加密方式+秘钥加密得到一个字符串。
将这三部分用.连接成一个完整的字符串,构成了最终的jwt。
2) jwt原理
jwt认证算法:签发与校验
"""
1)jwt分三段式:头.体.签名 (head.payload.sgin)
2)头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的
3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
"company": "公司信息",
...
}
5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
"user_id": 1,
...
}
6)签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
{
"head": "头的加密字符串",
"payload": "体的加密字符串",
"secret_key": "安全码"
}
'''
签发:根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token
"""
1)用基本信息存储json字典,采用base64算法加密得到 头字符串
2)用关键信息存储json字典,采用base64算法加密得到 体字符串
3)用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到 签名字符串
账号密码就能根据User表得到user对象,形成的三段字符串用 . 拼接成token返回给前台
"""
校验:根据客户端带token的请求 反解出 user 对象
"""
1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
"""
3) drf项目的jwt认证开发流程(重点)
"""
1)用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies中
2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户
注:登录接口需要做 认证 + 权限 两个局部禁用
"""
4) base64的编码和解码
base64的长度一定是4的倍数,如果不到用=补齐
import json
import base64
d={'typ': 'JWT','alg': 'HS256'}
s=json.dumps(d)
print(s)
#把字符串进行b64编码
res=base64.b64encode(bytes(s,encoding='utf-8'))
print(res)
#把b64编码的字符串,解码
b=b'eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9'
res=base64.b64decode(b)
print(res)
django中快速使用jwt
1) 官网
http://getblimp.github.io/django-rest-framework-jwt/
2) 安装
pip3 install djangorestframework-jwt
3) 使用:
# 1 创建超级用户
python3 manage.py createsuperuser
# 2 配置路由urls.py
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login/', obtain_jwt_token),
]
# 3 postman测试
向后端接口发送post请求,携带用户名密码(username,password),即可看到生成的token
4) 修改返回格式
要求登录成功返回格式:
{
code:100,msg:"登录成功",
token:asdfasdf,
username:lqz
}
写一个函数,在配置文件配置
def jwt_response_payload_handler(token, user=None, request=None):
return {'code': 100, 'msg': '登陆成功', 'token': token, 'username': user.username}
配置文件配置
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',
}
jwt的验证
只需要在视图类中加入一个认证类,一个权限类
class BookView(ViewSet):
authentication_classes = [JSONWebTokenAuthentication,]
permission_classes = [IsAuthenticated,]
需要在请求头中携带token
-请求头中key:Authorization
-请求头的value值:jwt token串
自定义用户表签发token
自己定义用户表,实现签发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.decorators import action
class UserView(ViewSet):
@action(methods=['POST'],detail=False)
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = UserInfo.objects.filter(username=username, password=password).first()
if user:
# 登录成功---》签发token
payload = jwt_payload_handler(user) # 根据当前登录用户获取荷载
token = jwt_encode_handler(payload) # 根据荷载生成token
return Response({'code': 100, 'msg': '登录成功', 'token': token})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
自定义认证类验证token
从JSONWebTokenAuthentication的父类BaseJSONWebTokenAuthentication里的authenticate方法里找源码,编写认证类。
import jwt
from .models import UserInfo
from rest_framework_jwt.settings import api_settings
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
token=request.META.get('HTTP_TOKEN')
try:
payload = jwt_decode_handler(token)
except jwt.ExpiredSignature:
msg = 'token过期了'
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = 'token解码错误'
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
msg = '解析token未知错误'
raise exceptions.AuthenticationFailed(msg)
# try:
# payload = jwt_decode_handler(token)
# except Exception:
# raise exceptions.AuthenticationFailed('token错误')
user = UserInfo.objects.filter(pk=payload['user_id']).first()
return user, token


浙公网安备 33010602011771号