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>
posted @   扶我上码  阅读(13)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示