drf——分页、jwt介绍与原理、jwt快速使用、jwt源码分析、jwt自定义返回格式、自定义用户签发token、自定义token认证类

系列文章目录

第一章 django web开发模式、api接口、api接口测试工具、restful规范、序列化反序列化、drf安装使用

第二章 drf的使用、APIView源码分析、Request源码分析、Serializer的序列化

第三章 Serializer的反序列化、字段与参数、局部与全局钩子、ModelSerializer使用

第五章 drf认证、权限、频率源码分析、全局异常处理、自动生成接口文档、RBAC介绍



一、分页

分页是针对于查询所有的接口使用的,针对于分页需要在GenericAPIView的基础上设置对应的pagination_class来启动分页功能

分页在drf中有三种:

PageNumberPagination
LimitOffsetPagination
CursorPagination

1.PageNumberPagination

基本分页:正常的查第几页,每页显示多少条的方式,有前一页和下一页的url(常用)

使用时需要创建一个类继承PageNumberPagination然后设置对应的类属性

pagtions.py

class CommPageNumberPagination(PageNumberPagination):

    page_size = 2  # 每页显示的内容数
    page_query_param = 'page' # 当前在第几页的参数如:?page=5
    page_size_query_param = 'size' # 分页显示的内容数如: ?size=3 不能超过最大内容显示数
    max_page_size = 5 # 最大内容显示数

views.py

class BookView(GenericViewSet,ListModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer
    pagination_class = pagtions.CommPageNumberPagination #设置对应的分页类

分页返回的响应为:

"count": 6,  第几页数据
    "next": "http://127.0.0.1:8000/api/v1/book/?page=2", 下一页的url
    "previous": null, 上一页的url
    "results": [
        { 此处为该页对应显示的数据条数
            "id": 1, 
            "name": "asd1",
            "price": "12.56"
        },
    }

2.LimitOffsetPagination

偏移分页是根据当前页面来计算第一条坐标然后根据偏移量来获取当前页的数据

pagtions.py

class commLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2  # 每页显示多少条
    limit_query_param = 'limit'  # 取多少条
    offset_query_param = 'offset'  # 从第0个位置偏移多少开始取数据
    max_limit = 5 # 一页最多显示数据数

views.py

class BookView1(GenericViewSet,ListModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer
    pagination_class = pagtions.commLimitOffsetPagination

分页返回的响应为:

"count": 6,  第几页数据
    "next": "http://127.0.0.1:8000/api/v1/book/?page=2", 下一页的url
    "previous": null, 上一页的url
    "results": [
        { 此处为该页对应显示的数据条数
            "id": 1, 
            "name": "asd1",
            "price": "12.56"
        },
    }

3.CursorPagination

游标分页 不能精准跳转到某一页,只能在前一页或者下一页 但是因为是游标所以查询速度很快

pagtions.py

class commCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'  # 查询的名字  等同于  page=xx
    page_size = 2  # 每页显示多少条
    ordering = 'id' # 排序规则,必须是表中有的字段,一般用id

views.py

class BookView2(GenericViewSet,ListModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookSerializer
    pagination_class = pagtions.commCursorPagination

返回的响应结果

{
    "next": "http://127.0.0.1:8000/api/v1/book2/?cursor=cD0y",  下一页的url
    "previous": null, 上一页的url
    "results": [ 返回的当前页的数据
        {
            "id": 1,
            "name": "asd1",
            "price": "12.56"
        },
        {
            "id": 2,
            "name": "asd2",
            "price": "13.56"
        }
    ]
}

4.继承APIView实现分页

相对于GenericAPIView而言APIView需要我们自己使用分页类来进行分页数据的组装

第一种方式实例化自建分页类后调用分页类的方法拼凑

class BookView3(ViewSet):

    def list(self, request):
        books = models.Book.objects.all()
        paginator= pagtions.CommPageNumberPagination()
        qs = paginator.paginate_queryset(books, request, self) # 分页后的数据
        res = serializer.BookSerializer(qs, many=True) # 序列化数据
        # 第一种返回方式
        return Response({
        'code':200, 
        'msg':'查询全部书籍成功',  
        'count':books.count(), 
        'data':res.data, 
        'next': paginator.get_next_link(), 
        'previous':paginator.get_previous_link()
        })

第二种方式实例化自建分页类后使用分页类的方法获取最后的结果

class BookView3(ViewSet):

    def list(self, request):
        books = models.Book.objects.all()
        paginator= pagtions.CommPageNumberPagination()
        qs = paginator.paginate_queryset(books, request, self) # 分页后的数据
        res = serializer.BookSerializer(qs, many=True) # 序列化数据
		return paginator.get_paginated_response(res.data)

二、jwt介绍与原理

cookie是存放在客户端浏览器的键值对,主要目的是为了完成信息交互,为用户展示个性化的页面
使用cookie,容易被伪造,因为cookie是存放在客户端的之后就开发了session
session是存放在服务端的键值对,但是对于服务器的内存占用过大
所以出现了token,token是存放在客户端的一个加密字符串,用户访问服务器时携带上token来展示用户自己的身份,方便服务器发送对应数据

jwt主要作用是进行交互式通信的,可以动态的为用户展示个性化的页面,jwt集成了token的生成、加密解密、认证
token:
字符串:分三段
第一段:为头,软件所属公司,加密方式等
第二段:为荷载,放用户信息如{id:3,name:lqz}
第三段:为签名,把第一段和第二段通过某种加密方式+秘钥加密得到一个字符串

token典型样子:每个点最为字符串各阶段的分割
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

jwt的认证原理拿到第一段和第二段使用同样的加密方式+秘钥再加密,得到字符串跟第三段比较,如果一样,表示没有被篡改,如果不一样,表明被篡改了,token不能用了,如果没有被篡改,取出第二段 当前登录用户的信息,这个时候传递给认证类来进行认证,认证完成之后在进行别的正常数据交互处理

jwt组成
第一段 header为{‘typ’: ‘JWT’,‘alg’: ‘HS256’}
第二段 payload为荷载是真正用户的数据
{
“sub”: “1234567890”, # 过期时间
“id”:3
“name”: “John Doe”,
“admin”: true
}
第三段 signature为签名
header (base64编码后的)
payload (base64编码后的)
secret


三、jwt快速使用

1.首先是安装jwt

pip3 install djangorestframework-jwt

2.使用的表是django的auth的user表创建一个用户

3.配置路由
urls.py

from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
    path('login/', obtain_jwt_token),
]

4.向login发送post请求携带上对应的用户名密码这是会返回一个token

在这里插入图片描述


四、jwt源码分析

根据from rest_framework_jwt.views import obtain_jwt_token寻找到ObtainJSONWebToken,该类用于返回一个基于APIView的已登陆用户的token

class ObtainJSONWebToken(JSONWebTokenAPIView):
	此处为jwt自身的序列器
    serializer_class = JSONWebTokenSerializer
class JSONWebTokenSerializer(Serializer):
    def __init__(self, *args, **kwargs):
        super(JSONWebTokenSerializer, self).__init__(*args, **kwargs)

        self.fields[self.username_field] = serializers.CharField()
        self.fields['password'] = PasswordField(write_only=True)

	该方法返回了项目中正在活跃的用户(已登录)
    @property
    def username_field(self):
        return get_username_field()

	全局钩子函数
    def validate(self, attrs):
    	credentials 中获取到了post请求携带的用户名以及密码
        credentials = {
            self.username_field: attrs.get(self.username_field),
            'password': attrs.get('password')
        }
		此处获取credentials中所有的vlaue调用jwt的authenticate方法来获取user
        if all(credentials.values()):
            user = authenticate(**credentials)

            if user:
                if not user.is_active:
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)

                payload = jwt_payload_handler(user)

                return {
                    '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)

VerifyJSONWebToken是用于检验token是否有效的

class VerifyJSONWebToken(JSONWebTokenAPIView):
    serializer_class = VerifyJSONWebTokenSerializer
class VerifyJSONWebTokenSerializer(VerificationBaseSerializer):
    def validate(self, attrs):
        token = attrs['token']

        payload = self._check_payload(token=token) 根据token获取有效荷载(也就是用户的数据部分)
        user = self._check_user(payload=payload) 根据有效数据部分获取对应的用户

        return {
            'token': token,
            'user': user
        }

RefreshJSONWebToken将基于视图来判断登录用户是否还在活跃,如果还在活跃则会刷新已到期的token返回新的token给用户

class RefreshJSONWebToken(JSONWebTokenAPIView):
    serializer_class = RefreshJSONWebTokenSerializer
class RefreshJSONWebTokenSerializer(VerificationBaseSerializer):
    def validate(self, attrs):
        token = attrs['token']

        payload = self._check_payload(token=token)
        user = self._check_user(payload=payload)
        # Get and check 'orig_iat'
        orig_iat = payload.get('orig_iat')

        if orig_iat:
            # Verify expiration
            refresh_limit = api_settings.JWT_REFRESH_EXPIRATION_DELTA

            if isinstance(refresh_limit, timedelta):
                refresh_limit = (refresh_limit.days * 24 * 3600 +
                                 refresh_limit.seconds)

            expiration_timestamp = orig_iat + int(refresh_limit)
            now_timestamp = timegm(datetime.utcnow().utctimetuple())

            if now_timestamp > expiration_timestamp:
                msg = _('Refresh has expired.')
                raise serializers.ValidationError(msg)
        else:
            msg = _('orig_iat field is required.')
            raise serializers.ValidationError(msg)

        new_payload = jwt_payload_handler(user)
        new_payload['orig_iat'] = orig_iat

        return {
            'token': jwt_encode_handler(new_payload),
            'user': user
        }


五、jwt自定义返回格式

jwt的返回格式不一定符合我们的要求,这个时候我们需要通过修改配置文件告诉jwt我们需要的返回格式

首先创建一个函数

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.jwt_response.Jwt_response',
}

六、jwt自定义签发token

from django.contrib.auth.models import auth
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class CustomUser(ViewSet):
    def login(self, request):
        dicts = {'code': 200, 'msg':'登陆成功'}
        user = auth.authenticate(request, username=request.data.get('username'), password = request.data.get('password'))
        if user:
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            dicts['token'] = token
        else:
            dicts['code'] = 1001
            dicts['msg'] = '用户名或密码错误'
        return Response(dicts)

七、jwt自定义token认证类

只需要继承BaseAuthentication然后重写authenticate方法
在需要认证的类中配置authentication_classes即可

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from rest_framework_jwt.settings import api_settings
from app01.models import User
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
class CommAuthentication(BaseAuthentication):
    def authenticate(self, request):
        jwt_value = request.META.get('token')
        try:
            payload = jwt_decode_handler(jwt_value)
        except Exception:
            raise exceptions.AuthenticationFailed('token错误')
            user = User.objects.filter(pk=jwt_value['user_id']).first()
        return user, jwt_value
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Django Rest Framework(DRF)是一种基于 DjangoWeb 应用程序开发框架,它提供了许多工具和库,使得开发 Web API 更加容易。JWT 是一种基于 JSONWeb Token,它用于在网络应用程序和服务器之间传递声明以使身份验证和授权更加安全。在 DRF使用 JWT 可以使得 API 更加安全。 JWT 由三个部分组成:头部、载荷和签名。头部通常包含算法和令牌型的信息,载荷通常包含用户标识信息和令牌的过期时间,签名则是根据头部、载荷和密钥生成的。 DRF 支持 JWT 验证,可以使用第三方库 djangorestframework-jwt 来实现。实现方式如下: 1. 安装 djangorestframework-jwt ``` pip install djangorestframework-jwt ``` 2. 添加 JWT 相关配置到 settings.py 中 ``` REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ], } JWT_AUTH = { 'JWT_SECRET_KEY': SECRET_KEY, 'JWT_ALGORITHM': 'HS256', 'JWT_VERIFY_EXPIRATION': True, 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), 'JWT_ALLOW_REFRESH': True, 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=30), } ``` 3. 在 urls.py 中添加 JWT 相关路由 ``` from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token urlpatterns = [ url(r'^api-token-auth/', obtain_jwt_token), url(r'^api-token-refresh/', refresh_jwt_token), url(r'^api-token-verify/', verify_jwt_token), ] ``` 4. 在需要验证的视图中添加 @jwt_authentication_classes 装饰器 ``` from rest_framework.decorators import api_view, permission_classes, jwt_authentication_classes from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response @api_view(['GET']) @permission_classes([IsAuthenticated]) @jwt_authentication_classes def my_view(request): content = {'message': 'Hello, World!'} return Response(content) ``` 以上是 DRF使用 JWT 的简单介绍JWT原理是将用户标识信息和过期时间等信息进行编码,生成一个安全的 token,并将其传递给客户端。客户端在后续的请求中携带这个 token,在服务端进行验证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值