DRF 源码解析 权限组件(三)

71 阅读3分钟

概述

认证组件 = [认证类, 认证类, 认证类...]

  1. 执行每个认证类实例的authenticate()方法
  2. 认证成功或失败,不会执行后续的认证类实例
  3. 返回None, 执行后续认证类实例的authenticate()方法

权限组件 = [权限类,权限类,权限类...]

  1. 执行每个权限类实例的has_permission()方法
  2. 执行全部的权限类实例
  3. 默认情况下,保证所有权限类实例的has_permission()方法都返回True。只要有一个权限类返回False,则不能获取权限

如果希望权限组件可以像认证组件一样使用"或"而不是"且"的关系,可以在源码基础之上扩展+自定义。

权限组件应用

自定义权限类

from rest_framework.permissions import BasePermission

class Mypermission(Basepermission):
    # 权限类必须有message属性
    
    def has_permission(self, request, view):
        # 根据请求的数据,进行校验

自定义的权限类继承自BasePermission

class BasePermission(metaclass=BasePermissionMetaclass):  
# 所有权限类都应从中继承的基类。
  
    def has_permission(self, request, view):  
    # 如果授予了权限,则返回`True`,否则返回`False`
        return True  
  
    def has_object_permission(self, request, view, obj):  
     # 如果授予了权限,则返回`True`,否则返回`False`
        return True

给视图类添加自定义的权限组件

class OrderView(APIView):
    permission_classes = [MyPermission, ]  
    ...

也可以在全局设置默认的权限组件

REST_FRAMEWORK = {
    ...
    DEFAULT_PERMISSION_CLASSES: [
        "app01.permissions.MyPermission",
        ...
    ],
    ...
}

源码执行流程

class APIView(View):  
    ...
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES  
    ...
    def dispatch(self, request, *args, **kwargs):  
        ... 
        # 对request进行封装
        request = self.initialize_request(request, *args, **kwargs)  
        ...
        try:  
            # 执行权限的校验
            self.initial(request, *args, **kwargs)  
 
            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
        
    def initial(self, request, *args, **kwargs):  
        self.perform_authentication(request) 
        # 执行权限组件
        # 此时已经认证成功或者是匿名用户
        self.check_permissions(request)  
        self.check_throttles(request)
        
    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),  
                    # 自定义的权限类应该有message属性,内容是错误信息
                    code=getattr(permission, 'code', None)  
                    # 同样可以设置自定义权限类的code属性
                )
    
    def get_permissions(self):  
        # 返回权限类实例的列表
        return [permission() for permission in self.permission_classes]
        
    def permission_denied(self, request, message=None, code=None):  
        # 如果请求没有被允许,决定抛出哪种异常
        # 认证成功后,request.authenticators就是
        if request.authenticators and not request.successful_authenticator:  
            raise exceptions.NotAuthenticated()  
        raise exceptions.PermissionDenied(detail=message, code=code)
        
        
class Request:
    ...
    @property  
    def successful_authenticator(self): 
        # 返回被用于认证请求的认证类实例,或者None
        if not hasattr(self, '_authenticator'):  
            with wrap_attributeerrors():  
                self._authenticate()  
        return self._authenticator

如果希望只要有一个权限类通过即可,可以自定义:

class APIView(View):
    ...
    def check_permissions(self, request):
        for permission in self.get_permissions():
            # 这里是修改的地方
            # 只要有一个permission.has_permission()为True,就return
            if permission.has——permission(request, self):
                return
        # 如果一个权限类都没通过,那就抛出异常
        self.permission_denied(  
                    request,  
                    # 这里的permission是循环中的最后一个权限类实例
                    # 也可以自己选择哪个认证类的message和code
                    message=getattr(permission, 'message', None),  
                    # 自定义的权限类应该有message属性,内容是错误信息
                    code=getattr(permission, 'code', None)  
                    # 同样可以设置自定义权限类的code属性

注意:不要修改源码,在自己的视图内部重写check_permission()方法,或者自定义视图类让其他视图继承,覆盖APIView的同名方法。