drf权限组件(permissions)应用、源码分析、功能扩展

101 阅读2分钟

1.权限组件概述

在drf开发中,如果有些接口必须同时满足:A条件、B条件、C条件。 有些接口只需要满足:B条件、C条件,此时就可以利用权限组件来编写这些条件。

2.权限组件应用

默认情况下,须所有权限类中has_permission方法返回值均为True时,才能通过权限组件;这一点与认证组件不同。

2.1 编写权限类

from rest_framework.permissions import BasePermission


class UserPermission(BasePermission):
    message = {"status": False, 'msg': "无权访问1"}    # message使用详见源码分析部分,同时还是code。

    def has_permission(self, request, view):
        if request.user.role == 3:
            return True
        return False


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

    def has_permission(self, request, view):
        if request.user.role == 2:
            return True
        return False


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

    def has_permission(self, request, view):
        if request.user.role == 1:
            return True
        return False

2.2 在视图中进行应用

class UserView(NbApiView):
    # 经理、总监、用户
    permission_classes = [BossPermission, ManagerPermission, UserPermission]

    def get(self, request):
        print(request.user, request.auth)
        return Response("UserView")

    def post(self, request):
        print(request.user, request.auth)
        return Response("UserView")

3.权限组件源码分析

权限类参考第2.1 编写权限类部分内容

class APIView(View):
	permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES


    def check_permissions(self, request):

        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                )

    def get_permissions(self):
        return [permission() for permission in self.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 initial(self, request, *args, **kwargs):
        self.format_kwarg = self.get_format_suffix(**kwargs)
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme


        self.perform_authentication(request)

        # 核心代码-->2 执行check_permissions方法
        self.check_permissions(request)
        self.check_throttles(request)

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

        try:

        	# 核心代码-->1 
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = 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 UserView(APIView):
    permission_classes = []
    def get(self, request, *args, **kwargs):
        return Response('user')

4.权限组件扩展

  • 需求:将默认必须同时满足:A条件、B条件、C条件,修改为满足任意一个条件即可;
  • 思路:本质上是修改check_permissions方法;
  • 后续视图中集成以下代码中的MyApiView类即可。
from rest_framework.views import APIView


class MyApiView(APIView):
    def check_permissions(self, request):
        no_permission_objects = []
        for permission in self.get_permissions():

            if permission.has_permission(request, self):
                return
            else:
                no_permission_objects.append(permission)
        else:
            self.permission_denied(
                request,
                message=getattr(no_permission_objects[0], 'message', None),
                code=getattr(no_permission_objects[0], 'code', None)
            )