drf-权限组件的使用与源码分析

86 阅读4分钟

drf-权限组件的使用与源码分析

权限组件与认证有点类似

# 认证组件 = [认证类,认证类,认证类,]    -> 执行每个认证类中的authenticate方法;
- 认证成功或失败,不会在执行后续的认证类
- 返回None,执行后续的认证类。
- 或的关系
-------------------------------------------------------------------------------------------------
# 项目中某个请求必须满足:A条件、B条件、C条件。
# 权限组件 = [权限类,权限类,权限类..]   -> 执行所有权限类的has_permission方法,返回True通过、返回False表示不通过。
- 执行所有的权限类,遇到False就不会执行后续的权限类
- 且的关系
​
默认情况下,保证所有的权限类中的has_permission方法都返回True 。只要有一个返回False就视为验证失败
-------------------------------------------------------------------------------------------------
# 学会源码流程,扩展+自定义。让权限组件实现或的关系
# 项目中某个请求满足任意条件就行了:A条件、B条件、C条件。
- 执行所有的权限类,遇到True就不会执行后续的权限类
- 或的关系

局部使用方式(单个认证类举例):

自定义的权限类

返回True通过、返回False表示不通过。

from rest_framework.permissions import BasePermission
from rest_framework import exceptions
​
class MyPermission(BasePermission):
    # 自定义认证错误信息
    message = {'code': 10001, 'error': '你没权限'}
    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.
        获取多条数据的时候
        """
        if request.user:
            return True
        # 报错 ,返回给前端
        # raise exceptions.PermissionDenied({'code': 10001, 'error': '你没权限'})
        return False
​
    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        访问的是单条数据
        """
        return False

视图类

from ext.per import MyPermission
​
​
class OrderView(APIView):
    # 局部配置:权限类
    permission_classes = [MyPermission,]
    def get(self,request,*args,**kwargs):
        return Response('order')
​
​
class UserView(APIView):
    # 局部配置:权限类
    permission_classes = [MyPermission, ]
    def get(self,request,*args,**kwargs):
        return Response('user')

全局使用(单个认证类)

REST_FRAMEWORK = {
    # 全局配置:权限
    "DEFAULT_PERMISSION_CLASSES" : ["ext.per.MyPermission"],
}

源码分析

image-20220913114645628

源码详细逐步分析

①、请求进来是执行CBV的dispatch方法,这次我们只需要关注initial方法

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:
        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

image-20220913095013273

②、进入initial方法,倒数第二行的权限组件,check_permissions

def initial(self, request, *args, **kwargs):
​
    self.format_kwarg = self.get_format_suffix(**kwargs)
​
    # Perform content negotiation and store the accepted info on the request
    neg = self.perform_content_negotiation(request)
    request.accepted_renderer, request.accepted_media_type = neg
​
    # Determine the API version, if versioning is in use.
    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme
​
    # Ensure that the incoming request is permitted
    self.perform_authentication(request)
    self.check_permissions(request)
    self.check_throttles(request)

image-20220913144029688

③、进入check_permissions方法,重点要了解self.get_permissions这个方法

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)
            )

④、进入self.get_permissions,与认证组件类似,就是获取权限类的实例化对象列表

def get_permissions(self):
​
    return [permission() for permission in self.permission_classes]

⑤回到上一个③中的check_permissions方法

意思是说,如果权限认证不通过,则会执行permission_denied方法

image-20220913111316654

⑥、进入到如果有这个has_permission方法执行的permission_denied方法

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)

代码是指如果是认证失败则报认证失败的错误,否则报权限失败的错误,所以可以提前设置一个类的属性来替换权限不通过的报错信息

根据源码更改成或关系

本质:在需要应用的类中重写check_permissions方法

将不成功的认证放进一个列表里,然后如果for循环中最终没有返回,那么就会执行报错的函数permission_denied,其中报错信息为不通过的认证的第一个报错信息

def check_permissions(self, request):
    no_permission_list = []
    for permission in self.get_permissions():
        if permission.has_permission(request, self):
            return
        no_permission_list.append(permission)

    else:
        self.permission_denied(
            request,
            message=getattr(no_permission_list[0], 'message', None),
            code=getattr(no_permission_list[0], 'code', None)
        )

完整代码

ext 中新建 view.py,需要使用的类继承这个类即可

from rest_framework.views import APIView

class PermissionAPIView(APIView):
    def check_permissions(self, request):
        no_permission_list = []
        for permission in self.get_permissions():
            if permission.has_permission(request, self):
                return
            no_permission_list.append(permission)

        else:
            self.permission_denied(
                request,
                message=getattr(no_permission_list[0], 'message', None),
                code=getattr(no_permission_list[0], 'code', None)
            )

视图类

from ext.view import PermissionAPIView


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

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response({'status': True, 'msg': 'OrderView'})

权限类

class MyPermission1(BasePermission):
    message = {'status': False, 'error': '你没权限'}

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


class MyPermission2(BasePermission):
    message = {'status': False, 'error': '你没权限'}

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


class MyPermission3(BasePermission):
    message = {'status': False, 'error': '你没权限'}

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

多个权限类实现案例(基于或关系)

示例model表

class UserInfo(models.Model):
    username = models.CharField(verbose_name="用户名", max_length=64)
    password = models.CharField(verbose_name="密码", max_length=64)
    token = models.CharField(verbose_name="token", max_length=64, null=True, blank=True)
    role = models.IntegerField(verbose_name='角色', choices=((1, "总监"), (2, "经理"), (3, "员工")), default=1)

自定义的权限类

from rest_framework.permissions import BasePermission
from rest_framework import exceptions


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

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


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

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


class UserPermission(BasePermission):
    message = {'status': False, 'error': '无权访问'}

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

视图类

from ext.view import PermissionAPIView


# 用户和经理能访问
class UserInfoView(PermissionAPIView):
    permission_classes = [UserPermission, ManagerPermission, BossPermission]

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response({'status': True, 'msg': 'UserInfoView'})


# 经理和总监能访问
class OrderView(PermissionAPIView):
    permission_classes = [ManagerPermission, BossPermission, ]

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response({'status': True, 'msg': 'OrderView'})

image-20220913134445193