drf 07——分页、jwt
目录
1 三种分页方式
# 什么样的接口要分页----获取所有的接口 list方法
继承GenericAPIView, ListModelMixin
1.1 分页的使用
page.py
from rest_framework.pagination import PageNumberPagination
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.pagination import CursorPagination
# 基本分页
正常的查第几页,每页显示多少条的方式 -----常用
class CommonPageNumberPagination(PageNumberPagination):
page_size = 2 # 每页显示条数
page_query_param = 'page' # 查询页码参数 ?page=10
page_size_query_param = 'size' # ?page=3&size=5000
max_page_size = 5 # 可以通过size控制每页显示的条数,但是通过这个参数控制最多显示多少条
# http://127.0.0.1:8000/books/?page=1&size=300000
# 偏移分页
class CommonLimitOffsetPagination(LimitOffsetPagination):
default_limit = 3 # 每页显示条数
limit_query_param = 'limit' # 取多少条
offset_query_param = 'offset' #从第0个位置偏移多少开始取数据
max_limit = 5 # 最大限制条数
# http://127.0.0.1:8000/books/?limit=3&offset=3 # 从第三条开始往后取3条
# 游标分页
针对于大数据量分页效率高、可控性差--->只能选择上一页和下一页,不能直接跳转到某一个
class CommonCursorPagination(CursorPagination):
cursor_query_param = 'cursor' # 查询的名字 等同于 page=xx
page_size = 4 # 每页显示多少条
ordering = 'id' # 排序规则,必须是表中有的字段,一般用id
# http://127.0.0.1:8000/books/?cursor=cD0z
视图类
# 三种分页方式:
from .page import CommonPageNumberPagination
from .page import CommonLimitOffsetPagination
from .page import CommonCursorPagination
class BookView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
# pagination_class = CommonPageNumberPagination
# pagination_class = CommonLimitOffsetPagination
pagination_class = CommonCursorPagination
1.2 继承APIView实现分页
class BookView(ViewSet):
def list(self, request):
books = Book.objects.all()
# 分页
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)
2 jwt介绍和原理
# jwt: Json web token
# cookie,session,token
-cookie是存放在客户端浏览器的键值对
-session是存放在服务端的键值对
客户端:sessionId:asdfasdf----》放到cookie中
服务端:
asdfasdf:{id:3,name:lqz}
dddddd:{id:4,name:pyy}
-token: 字符串:分三段
-第一段:头--->公司信息,加密方式。。。
-第二段:荷载--->放用户信息----> {id:3,name:lqz}
-第三段:签名--->把第一段和第二段通过某种加密方式+秘钥加密得到一个字符串 asdfads
-把三段使用base64编码后拼到一起 dasfasd.asdfasdasd.asdfads
-【token 的签发】--->登录成功后,服务端生成
-把token串给前端--->前端拿着
-后面前端只要发请求,就携带token串到后端
-【token的验证】--->拿到第一段和第二段使用同样的加密方式+秘钥再加密--
->得到字符串跟第三段比较,如果一样,表示没有被篡改,
如果不一样,表明被篡改了,token不能用了
-如果没有被篡改,取出第二段 当前登录用户的信息 id
# jwt:Json web token--->只针对于web方向的token方式验证
# token典型样子
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
# jwt组成
-1 header--->{'typ': 'JWT','alg': 'HS256'}--->通过bas64编码后---->eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
-2 payload--->荷载--->真正用户的数据
{
"sub": "1234567890", # 过期时间
"id":3
"name": "John Doe",
"admin": true
}
-3 signature--->签名
header (base64后的)
payload (base64后的)
secret
# base64的编码和解码
-base64的长度一定是4的倍数,如果不到用=补齐
encode编码 字符类型---->二进制
decode解码 二进制---->字符类型
大多数的编码都是由字符转化成二进制的过程,
而从二进制转成字符的过程称为解码。
而Base64的概念就恰好反了,由二进制转到字符称为编码,由字符到二进制称为解码。
研究发现base64需要二进制进行编码 编码后的还是二进制
可以通过二进制直接解码 也可以二进制进行解码 解码后得到的的是二进制
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)
# 开发中使用jwt认证的重点
-签发token---》登录
-认证token---》认证类
3 django中快速使用jwt
# 安装
pip3 install djangorestframework-jwt
# 快速使用
使用django的auth的user表
先创建一个用户
# 只需要在路由中
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login/', obtain_jwt_token),
]
4 修改返回格式
# 返回格式
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InljaiIsImV4cCI6MTY1NTk4NTE3NywiZW1haWwiOiIifQ.h9y0Nxzd7NTO8WCnABswn3OUm2c2xDhuUvA4qc9TAbE"
}
# 想要修改成我们自己想要的样子
登录成功返回格式:
{code:100, msg:"登录成功", token:asdfasdf, username:xxx}
# 写一个函数 在配置文件中配置
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',
}
5 jwt的验证
# 只需要在视图类中加入一个认证类,一个权限类
class BookView(ViewSet):
authentication_classes = [JSONWebTokenAuthentication,]
permission_classes = [IsAuthenticated,]
# 需要在请求头中携带token
请求头中的k ----Authorization
请求头中的v ----jwt token串 # 中间空格
6 自定义用户表签发token
# 自己定义用户表,实现签发token功能--->登录接口
from rest_framework.decorators import action
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
# 127.0.0.1:8000/user/login ---->post请求
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) # 根据当前登录用户获取荷载
print(payload)
token = jwt_encode_handler(payload) # 根据荷载生成token
return Response({'code': 100, 'msg': '登录成功', 'token': token})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
6 自定义认证类验证token
from rest_framework_jwt.settings import api_settings
from rest_framework import exceptions
from .models import UserInfo
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):
# 1 取出 token
jwt_value = request.META.get('HTTP_TOKEN')
# 2 验证token是否合法
# try:
# payload = jwt_decode_handler(jwt_value)
# 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(jwt_value)
except Exception:
raise exceptions.AuthenticationFailed('token错误')
print(payload) # 荷载--->user_id
user = UserInfo.objects.filter(pk=payload['user_id']).first()
return user, jwt_value
补充
# 加密:对称加密 非对称加密
-加密和解密都使用同一个秘钥
-加密用公钥,解密用私钥
自定义jwt签发和认证
点击查看代码
from rest_framework.decorators import action
from .utils import my_jwt_encode_handler, my_jwt_payload_handler
# 127.0.0.1:8000/user/login ---->post请求
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 = my_jwt_payload_handler(user) # 根据当前登录用户获取荷载
print('payload为======', payload)
token = my_jwt_encode_handler(payload) # 根据荷载生成token
return Response({'code': 100, 'msg': '登录成功', 'token': token})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
### 根据登录的用户获取荷载
def my_jwt_payload_handler(user):
payload = {
'user_id': user.pk,
'username': user.username,
'exp': str(datetime.utcnow()) # datetime类型无法直接序列化
}
return payload
### 自定义的jwt签发
import base64
import hashlib
import json
def my_jwt_encode_handler(payload):
header = {'typ': 'JWT','alg': 'HS256'} # 自定义的头
payload = payload
print('带时间的',payload)
# 字典转json格式
header_json = json.dumps(header)
payload_json = json.dumps(payload)
# header和payload进行base64编码 需要先转成二进制
header_base = base64.b64encode(bytes(header_json, encoding='utf-8'))
payload_base = base64.b64encode(bytes(payload_json, encoding='utf-8'))
# header和payload拼成sign json格式直接拼
sign = header_json + payload_json
# sign进行md5加密
md5 = hashlib.md5()
md5.update(sign.encode('utf8')) # 加密时需要先转二进制
sign_md5 = md5.hexdigest()
print('md5加密后的', sign_md5) # 获得字符串
# sign进行base64编码
sign_base = base64.b64encode(bytes(sign_md5, encoding='utf-8'))
# 三合一 加的.所有需先转字符类型
token = header_base.decode() + '.' + payload_base.decode() + '.' + sign_base.decode()
print('token====', token)
return token
### jwt认证
def my_jwt_decode_handler(token):
print('开始解密')
# 这三段是字符类型的
header_base, payload_base, sign_base = token.split('.')
print(header_base, payload_base, sign_base)
# 解码转回字典
print('获取成功')
header = base64.b64decode(header_base) # 获得b64解码后的二进制
payload = base64.b64decode(payload_base)
payload_dict_json = payload.decode() # 二进制转回json格式字符串
payload_dict = json.loads(payload_dict_json) # json转回字典
sign = header + payload # 获得二进制的sign
md5 = hashlib.md5()
md5.update(sign)
sign_md5 = md5.hexdigest() # 获得字符串
print('md5加密后的', sign_md5)
print('原先的第三段:',sign_base)
new_sign_base = base64.b64encode(bytes(sign_md5, encoding='utf-8'))
# 二进制转字符串
new_sign = new_sign_base.decode()
print('现在的第三段:', new_sign)
print('开始加密对比')
if new_sign == sign_base:
print('解密成功')
print('将要返回的payload:', payload_dict)
return payload_dict
print('解密失败')
</details>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?