权限源码流程

2020-07-21  本文已影响0人  小吉头

权限模块

rest_framework.permissions是restframework提供的权限模块,其他权限类都要继承BasePermission

权限验证流程

dispatch()方法中,有下面两段关键代码:

...
#封装Request对象
request = self.initialize_request(request, *args, **kwargs)
...
#认证、权限、限流验证
self.initial(request, *args, **kwargs)
...

initial()方法中进行了认证、权限、限流验证:

 def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        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) #限流

下面看下权限的流程

class APIView(View):
    ...
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES#跟认证流程类似,如果在settings.py中定义了`DEFAULT_PERMISSION_CLASSES`会覆盖restframework settings.py中的定义
    ...

    def permission_denied(self, request, message=None):
        """
        If request is not permitted, determine what kind of exception to raise.
        """
        if request.authenticators and not request.successful_authenticator:
            raise exceptions.NotAuthenticated()
        raise exceptions.PermissionDenied(detail=message)


    def get_permissions(self):
        """
        Instantiates and returns the list of permissions that this view requires.
        """
        return [permission() for permission in self.permission_classes]

    
     def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )

本例settings.py定义:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES':[
        'rest_framework.permissions.IsAuthenticated',
        'rest_framework.permissions.IsAdminUser'
    ]
}

所以for permission in self.get_permissions():中的self.get_permissions()就是[IsAuthenticated(),IsAdminUser()]

遍历list中对象,调用has_permission(request, self)进行认证,self指向封装的Request对象。返回False,权限认证失败。可以定义message属性实现权限验证失败自定义提示。
IsAuthenticated类为例,查看源码:

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

    def has_object_permission(self, request, view, obj):#RetrieveModelMixin获取单个对象,self.get_object()实际调用的是GenericAPIView中的get_object(),里面的self.check_object_permissions(self.request, obj)会检测是否有操作该对象的权限
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True


#认证通过才返回True
class IsAuthenticated(BasePermission):
    """
    Allows access only to authenticated users.
    """

    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated)

自定义权限类

根据验证认证的规则,自定义认证类,继承自BasePermission
utils目录下新建permission.py

from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):

    message = "用户没有访问权限"#自定义权限认证失败提示

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

全局配置

如果想让所有视图都必须要认证后才能访问,可以在django项目的settings.py中设置REST_FRAMEWORK变量,即本篇开始时写的,修改成自定义的认证类:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES':['utils.permission.MyPermission']
}

局部配置

如果只是某个视图使用,可以在类中定义属性,比如:permission_classes = [MyPermission]
如果不需要认证,可以写成permission_classes = []

上一篇下一篇

猜你喜欢

热点阅读