【Python Web 框架】Django023---drf ---jwt内置签发方法修改返回格式、token认证的源码分析、RBAC介绍

本文详细解析了Django Rest Framework中JWT的签发与认证流程,包括自定义签发与认证方法,并介绍了RBAC权限控制模型及其在Django中的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、过滤的源码分析

-视图类中配置类属性:filter_backends = ["过滤类"]
-必须继承ListAPIView(或ListModelMixin+GenericAPIView,因为ListAPIView=ListModelMixin+GenericAPIView)
-ListModelMixin的list方法中执行了self.filter_queryset,视图类
-GenericAPIView找filter_queryset方法
for backend in list(self.filter_backends):
    queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
上面就是解释了:我们配置的过滤类为啥会执行

2、分页类的源码分析

-视图类中配置类属性: pagination_class = 分页类(分页只有一个,不需要在列表中)
-必须继承他俩ListModelMixin+GenericAPIView
-ListModelMixin的list方法中执行了
page = self.paginate_queryset(queryset) # 分页
if page is not None:
   serializer = self.get_serializer(page, many=True)
   return self.get_paginated_response(serializer.data)

-GenericAPIView找paginate_queryset方法
	#self.paginator配置的分页类的对象
	self.paginator.paginate_queryset(queryset, self.request, view=self)
-GenericAPIView找get_paginated_response方法
	#self.paginator配置的分页类的对象
	self.paginator.get_paginated_response(data)
-具体的分页类:PageNumberPagination
	-paginate_queryset
    -get_paginated_response

3 继承APIView实现分页

-获取了所有数据
-分页
	-实例化得到分页对象
    -调用分页对象的paginate_queryset方法完成分页
    -把要分页的数据序列化
    -把序列化后的数据返回(直接返回和使用get_paginated_response)

4 自定义分页(出去面试,面试官问:如何自定义分页)

-使用当此请求的request对象,取出获取的页码数
-通过页码数和每页显示多少条,具体的取出当前页码的数据

5 jwt

json web token,是一种前后端的登录认证方式,它分为token的签发和认证,签发的意思是用户登录成功,生成三段式的token串;认证指的是用户访问某个接口,需要携带token串过来,我们完成认证

6 三段:头.荷载.签名,每一段都通过base64编码

7 base64的编码和解码

8 djangorestframework-jwt:好久没维护了,使用djangorestframework-simplejwt

9 djangorestframework-jwt快速使用

-快速签发
	-在路由中(登录功能有了,基于auth的user表实现的登录)
    path('login/', obtain_jwt_token)
-快速认证
	-在视图类中配置
    authentication_classes = [JSONWebTokenAuthentication, ] #drf_jwt
	permission_classes = [IsAuthenticated] # drf

还有一点:

实现国际化,除了要将配置文件改了,还要将第三方的APP注册一下,才能实现

在这里插入图片描述

jwt内置签发方法修改返回格式
现在用户一登录,只返回了token,但是实际情况下,需要返回好些用户信息
实现:
1、写一个函数
2、在配置文件中配置
在这里插入图片描述
在这里插入图片描述

上面函数就写好了,下面需要配置一下

在这里插入图片描述

读一下源码,为啥我们这样写,就能生效

在这里插入图片描述

源码(from rest_framework_jwt import settings)这里就写了一个token

jwt签发的源码分析

路由的====> obtain_jwt_token(from rest_framework_jwt.views import obtain_jwt_token)=====>
obtain_jwt_token = ObtainJSONWebToken.as_view()====>视图类
class ObtainJSONWebToken(JSONWebTokenAPIView):
    #序列化类
    serializer_class = JSONWebTokenSerializer
登录请求,发了一个post请求,携带用户名和密码====>所以ObtainJSONWebToken的post请求

在这里插入图片描述

def post(self, request, *args, **kwargs):
    # 得到一个序列化类的对象,使用传入的request.data
    serializer = self.get_serializer(data=request.data)
    # 序列化类对象的is_valid,会执行字段自己的校验规则,局部钩子,全局钩子
    if serializer.is_valid():
        # 取出user,取出token
        user = serializer.object.get('user') or request.user
        token = serializer.object.get('token')
        # 执行你在配置文件中配置的函数
        response_data = jwt_response_payload_handler(token, user, request)
        response = Response(response_data)
   return response

    =====> JSONWebTokenSerializer源码

在这里插入图片描述

    def validate(self, attrs):
        credentials = {
        self.username_field: attrs.get(self.username_field),
        'password': attrs.get('password')
        }       # 取出前端传过来的数据{“username”:“lqz”,“password”:“lqz123456”}

        if all(credentials.values()):
            user = authenticate(**credentials)  # 通过用户名密码认证auth的user表中的用户
            if user:
                if not user.is_active:
                    # 通过user得到荷载{user_id:1,username:lqz,...}
                    payload = jwt_payload_handler(user)
                    return {
                        'token': jwt_encode_handler(payload),   #通过荷载得到token串
                        'user': user
                    }

token认证的源码分析

写一个视图类

在这里插入图片描述

上面的写好之后,访问这个接口就需要携带着token过来了,并且携带token的方式要放在请求头中,并且是jwt空格token值(都是固定格式)
下面我们就去源码里面看看,为啥要这样
1、读认证类JSONWebTokenAuthentication的父类BaseJSONWebTokenAuthentication的authenticate方法

在这里插入图片描述

    class BaseJSONWebTokenAuthentication(BaseAuthentication):
        def authenticate(self, request):
            # 取出前端传入的Authorization的value值,token串
            jwt_value = self.get_jwt_value(request)
            if jwt_value is None: #如果token串,没有传,也认证通过。所以必须要加一个权限类。就是这里的原因
                return None

            try:
                payload = jwt_decode_handler(jwt_value) #通过token串获得payload,验签(看看是否被篡改),检查过期时间
            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()

            # 通过payload获得当前用户:通过user_id去user表中获取当前用户
            user = self.authenticate_credentials(payload)

            return (user, jwt_value)
2、JSONWebTokenAuthentication的get_jwt_value方法,返回token串
    class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):

        www_authenticate_realm = 'api'

        def get_jwt_value(self, request):
            #取出前端传入的token串,通过空格切分
            # (jwt,token串)
            auth = get_authorization_header(request).split()
            auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

            if not auth:    #如果没有传认证的token,返回none
                if api_settings.JWT_AUTH_COOKIE:
                    return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
                return None

            if smart_text(auth[0].lower()) != auth_header_prefix:
                return None

            if len(auth) == 1:# 元祖的长度等于1,抛异常,因为我们jwt token串
                msg = _('Invalid Authorization header. No credentials provided.')
                raise exceptions.AuthenticationFailed(msg)
            elif len(auth) > 2: #元祖的长度大于2,抛异常,因为我们jwt token串,应该是等于2
                msg = _('Invalid Authorization header. Credentials string '
                        'should not contain spaces.')
                raise exceptions.AuthenticationFailed(msg)

            return auth[1]


读完上面的源码,我们就可以自定义签发和认证token了。签发就是自己写一个登录,等过用户等到payload,通过payload得到token
验证的话,就是写一个认证类重写authenticate方法

自定义user表实现jwt的token签发

在这里插入图片描述
在这里插入图片描述

代码:
###########使用自己的用户表,签发token

from .models import UserInfo
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings  # 源码里面

jwt_payload_handle = api_settings.JWT_PAYLOAD_HANDLER  # 源码里面
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER  # 源码里面


class LoginView(APIView):
    def post(self, request):
        username = request.data.get("username")
        password = request.data.get("password")
        user = UserInfo.objects.filter(username=username, password=password).first()
        if user:  # 用户名和密码正确
            payload = jwt_payload_handle(user)  # 源码里面
            token = jwt_encode_handler(payload)  # 源码里面
            return Response({"code": 100, "msg": "登录成功", "token": token, "username": user.username})
        else:
            return Response({"code": 101, "msg": "用户名或密码错误"})

自定义user表实现jwt的token认证

上面完成了签发,下面就是需要认证了,就是写一个认证类:重写authenticate方法

在这里插入图片描述

下面在配置一下认证类

在这里插入图片描述
在这里插入图片描述

补充一点:

在这里插入图片描述

上面每次访问接口,都需要去数据库,拿当前用户。这样不好

在这里插入图片描述

这是一种方案

在这里插入图片描述

另一种方案
上面就是稍微优化一下,不是每次都去查询当前登录用户(源码中确实每次都会去查)

RBAC介绍

基于角色的访问控制:RBAC
是基于角色的访问控制(Role-Based Access Control )在 RBAC  中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。
这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。

rbac权限控制一般都是用在公司内部的管理系统中(python的django写公司内部项目较多,rbac很重要)
Django的 Auth组件 采用的认证规则就是RBAC

rbac表分析
    用户表:auth_user
    角色表(部门表,组表):auth_group
    权限表:auth_permission
    角色和权限是多对多(市场部这个角色,即可以报销出差,有可以申请出差,有很多权限。但是别的部门也可以有报销出差,申请出差的权限;所以多对多)=====>中间表:auth_group_permissions
    用户和角色也是多对多(一个用户即属于市场部,有属于人事部)=====>中间表:auth_user_groups
    其实这是django的rbac的表就结束了,但是
    django的auth多写了一个表,用户和权限的多对多(为了更细粒度的管理权限):auth_user_user_permissions
    所以一共6张表
    18

django有个admin后台管理,配合auth,可以快速搭建出一个基于rbac的后台管理系统

在这里插入图片描述

    现在是超级用户登录的,他有真删改查的权限,我们现在再新增一个用户,让他只有查看的权限
    增加用户,只能超级用户增

在这里插入图片描述

    现在这个用户对book表只有查看权限
    几年前,好多公司内部的管理系统,就是使用django的admin快速搭建的

admin不好看,第三方美化
    -django 1.x上很火的 Xadmin:前端基于jq+bootstrap,2.x上支持不好了,3.x直接不能用了,而且作者也弃坑了,他又做了一个前后端分离的admin
    -simple-ui:djanog 3.x

    我们现在需要将我们自己的django_admin 美化一下
    1、装一下:pip install django-simpleui
    2、把simpleui注册在第一行

在这里插入图片描述
在这里插入图片描述

    官网:https://simpleui.72wo.com/docs/simpleui/doc.html

在这里插入图片描述

    这里首页也可以显示内网的地址

在这里插入图片描述

    但是这样还是不好看,可以搞一些大屏,直线图
    这就需要集成echars(号称5分钟上手,hichars号称1分钟上手)
    echars(https://echarts.apache.org/zh/index.html)原来是百度做的,后来给了Apache基金会,成了Apache顶级的开源项目(kafka也是Apache项目)
    copy这里:https://echarts.apache.org/handbook/zh/get-started/

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初遇我ㄖ寸の热情呢?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值