DRF之权限组件

  • 认证组件 = [认证类,认证类,认证类,] -> 执行每一个认证类的authenticate方法

    • 认证成功或失败,不会再执行后续的认证类

    • 返回None,执行后续认证类

  • 权限组件 = [权限类,权限类,权限类,] -> 执行所有权限类的has_permission方法,返回True通过,返回False不通过

    • 执行所有的权限类

默认情况下,要保证所有的权限类中的has_permission方法都返回True,即的关系

掌握源码流程后,可以扩展+自定义,实现其他逻辑

1. 快速使用

  • 编写类
class BasePermission(metaclass=BasePermissionMetaclass):
    """
    A base class from which all permission classes should inherit.
    """

    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True

阅读源码可知,自定义的权限类的has_permission应返回True或False来表示是否有权限

# ext/
from rest_framework.permissions import BasePermission
import random


class MyPermission(BasePermission):
    def has_permission(self, request, view):
        # 获取请求中的数据,然后进行校验...
        v1 = random.randint(1, 3)
        if v1 == 2:
            return True
        return False
  • 应用类

全局应用 在settings.py中配置

局部应用 在视图函数中定义permission_classes = []参数

根据业务需求灵活配置

2. 错误信息和多个权限类

  • 在权限类中自定义错误信息message
class MyPermission(BasePermission):
    message = {"status": False, "msg": "无权访问"}

    def has_permission(self, request, view):
        # 获取请求中的数据,然后进行校验...
        v1 = random.randint(1, 3)
        if v1 == 2:
            return True
        return False

左图为默认错误信息,右图为自定义错误信息

  • 多个权限类

api/per.py

from rest_framework.permissions import BasePermission


class MyPermission1(BasePermission):
    message = {"status": False, "msg": "无权访问"}

    def has_permission(self, request, view):
        print("MyPermission1")
        return False


class MyPermission2(BasePermission):
    message = {"status": False, "msg": "无权访问"}

    def has_permission(self, request, view):
        print("MyPermission2")
        return False


class MyPermission3(BasePermission):
    message = {"status": False, "msg": "无权访问"}

    def has_permission(self, request, view):
        print("MyPermission3")
        return False

api/views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from ext.per import MyPermission1, MyPermission2, MyPermission3


class OrderView(APIView):
    authentication_classes = []
    permission_classes = [MyPermission1, MyPermission2, MyPermission3]

    def get(self, request):
        print(request.user, request.auth)
        return Response({"status": True, "data": [11, 22, 33, 44]})

终端只打印了MyPermission1

说明程序会按顺序校验每个权限类,一旦有一个返回False即校验不通过,则不再校验后续权限类,只有通过每一个权限类的校验才算有权限访问

3. 源码流程

as_view()等方法的调用在认证组件中已详细阐明,此处直接从dispatch()方法开始

class APIView(View):
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES  # 权限组件的全局配置

    def permission_denied(self, request, message=None, code=None):
        if request.authenticators and not request.successful_authenticator:
            raise exceptions.NotAuthenticated()
        raise exceptions.PermissionDenied(detail=message, code=code)

    def get_permissions(self):
        # 获取权限类对象的列表
        return [permission() for permission in self.permission_classes]

    def check_permissions(self, request):
        # 循环每一个权限类对象,判断权限校验是否通过
        for permission in self.get_permissions():
            # 如果未通过,则抛出异常在dispatch中捕获
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                )

    def initial(self, request, *args, **kwargs):
        self.perform_authentication(request)  # 认证组件的过程,循环执行每个authenticate方法;失败则抛出异常不向下执行
        self.check_permissions(request)  # 权限的校验
        self.check_throttles(request)

    def dispatch(self, request, *args, **kwargs):
        # 封装drf的request
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)
            # 反射获取get/post等方法
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            # 执行相应方法
            response = handler(request, *args, **kwargs)
        # 异常处理
        except Exception as exc:
            response = self.handle_exception(exc)
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response


class OrderView(APIView):
    permission_classes = [MyPermission1, MyPermission2, MyPermission3]  # 权限组件的局部配置

    def get(self, request):
        print(request.user, request.auth)
        return Response({"status": True, "data": [11, 22, 33, 44]})

4. 组件的拓展

通过源码可以知道,在check_permissions中,只要有一个权限类校验不通过就是无权访问,那么如何才能实现拓展,使其满足只要有一个权限类通过就是有权限呢?显而易见需要重写check_permissions方法

class OrderView(APIView):
    authentication_classes = []
    permission_classes = [MyPermission1, MyPermission2, MyPermission3]

    def get(self, request):
        print(request.user, request.auth)
        return Response({"status": True, "data": [11, 22, 33, 44]})

    def check_permissions(self, request):
        no_permission_objects_list = []
        for permission in self.get_permissions():
            if permission.has_permission(request, self):
                return
            else:
                no_permission_objects_list.append(permission)
        else:
            self.permission_denied(
                request,
                message=getattr(no_permission_objects_list[0], 'message', None),
                code=getattr(no_permission_objects_list[0], 'code', None)
            )

OrderView视图中重写check_permissions方法,则在权限校验的生命周期中,自定义的check_permissions便覆盖了APIView中的默认check_permissions实现逻辑,达到自定义拓展的效果

自定义的check_permissions实现了一旦某一权限类校验通过,就不再进行后续校验。若所有类均未通过,则返回第一个未通过的类的错误信息

由此可见学习源码的重要性,不仅可以学习源码中优秀的代码编写方式,还可以在原有逻辑的基础上进行拓展

上述是修改一个视图函数的权限校验逻辑,如何应用到其他视图呢?

可以自定义一个NbApiView的类,继承APIView并且重写check_permissions,之后要实现或逻辑的权限认证的视图只需继承NbApiView即可

  • ext/view.py
from rest_framework.views import APIView


class NbApiView(APIView):
    def check_permissions(self, request):
        no_permission_objects_list = []
        for permission in self.get_permissions():
            if permission.has_permission(request, self):
                return
            else:
                no_permission_objects_list.append(permission)
        else:
            self.permission_denied(
                request,
                message=getattr(no_permission_objects_list[0], 'message', None),
                code=getattr(no_permission_objects_list[0], 'code', None)
            )
  • api/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from ext.per import MyPermission1, MyPermission2, MyPermission3
from ext.view import NbApiView


class OrderView(NbApiView):
    authentication_classes = []
    permission_classes = [MyPermission1, MyPermission2, MyPermission3]

    def get(self, request):
        print(request.user, request.auth)
        return Response({"status": True, "data": [11, 22, 33, 44]})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值