三、REST framework的三大组件之一:权限

1、概述

权限组件与认证组件用法大致一样,及 权限组件=[权限类1、权限类2、权限类3..],权限组件默认情况下:当所有的权限类全部为True时,才通过权限<后续我们修改为或的关系>

2、在ext中新建MyPermission.py,并添加权限类

# 导入权限rest_framework的权限类
from rest_framework.permissions import BasePermission


# 新建权限类,并继承BasePermission
class MyPermission1(BasePermission):

    # 结合源码解释
    def has_permission(self, request, view):
        # 获取请求中的数据,然后进行校验,先了解,下面会介绍用法
        print('MyPermission1')
        role = 1
        if role == 1:
            return True
        return False

3、应用权限类,并使用postman访问接口

# 导入权限类
from .ext.MyPermission import MyPermission1


# 修改认证类创建的UserView, 添加权限
class UserView(MyAPIView):

    # permission_classes 为权限类的默认配置参数,必须怎么写,详细过程可以查看源码
    # 当不使用权限类时,列表为空
    # 请注意,当类中使用permission_classes=[权限类1, 权限类2, ...]时,权限默认使用就近原则,使用类中的permission_classes
    permission_classes = [MyPermission1]

    def get(self, request, *args, **kwargs):
        return Response('GET')

# postman访问接口,接口测试通过

4、修改权限类,if role==2,并再次访问接口

# 导入rest_framework的权限类
from rest_framework.permissions import BasePermission


# 新建权限类,并继承BasePermission
class MyPermission1(BasePermission):

    # 结合源码解释
    def has_permission(self, request, view):
        # 获取请求中的数据,然后进行校验,先了解,下面会介绍用法
        print('MyPermission1')
        role = 1
        if role == 2:
            return True
        return False

# postman访问接口,接口测试失败
# 返回:
# {
#     "detail": "您没有执行该操作的权限。"
# }

5、权限和认证一样都支持全局配置

REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,
    "DEFAULT_AUTHENTICATION_CLASSES": ["auto_project.ext.myAuthentication.QueryParamsAuthentication",
                                       "auto_project.ext.myAuthentication.HeaderAuthentication",
                                       "auto_project.ext.myAuthentication.BodyAuthentication",
                                       "auto_project.ext.myAuthentication.NoAuthentication",
                                       ],
    # 添加DEFAULT_PERMISSION_CLASSES,所有视图默认全部应用权限
    "DEFAULT_PERMISSION_CLASSES": ["auto_project.ext.MyPermission.MyPermission1"]
}


# 修改UserView 备注掉permission_classes
# 导入认证类
from .ext.MyPermission import MyPermission1


# 修改认证类创建的UserView, 添加权限
class UserView(MyAPIView):

    # permission_classes 为权限类的默认配置参数,必须怎么写,详细过程可以查看源码
    # 当不使用权限类时,列表为空
    # 请注意,当类中使用permission_classes=[权限类1, 权限类2, ...]时,权限默认使用就近原则,使用类中的permission_classes
    # permission_classes = [MyPermission1]

    def get(self, request, *args, **kwargs):
        return Response('GET')

# postman访问接口,接口测试失败
# 返回
# {
#    "detail": "您没有执行该操作的权限。"
# }

6、通过上述步骤,发现权限失败时,接口返回默认信息,默认信息可以通过 message就行修改

# 导入权限rest_framework的权限类
from rest_framework.permissions import BasePermission


# 新建权限类,并继承BasePermission
class MyPermission1(BasePermission):

    # 配置接口没有权限时,返回结果
    message = {'code': 100003, 'msg': '权限错误'}

    # 结合源码解释
    def has_permission(self, request, view):
        # 获取请求中的数据,然后进行校验,先了解,下面会介绍用法
        print('MyPermission1')
        role = 1
        if role == 2:
            return True
        return False

# postman访问接口,接口测试失败
# 返回:
# {
#     "code": "100003",
#     "msg": "权限错误"
# }

7、修改源码,修改为或的关系

7.1、在概述中,我们说过,权限组件默认情况下:当所有的权限类全部为True时,才通过权限,验证是否是这样,添加三个权限类,并使用

# 导入rest_framework的权限类
from rest_framework.permissions import BasePermission


# 新建权限类,并继承BasePermission
class MyPermission1(BasePermission):

    # 配置接口没有权限时,返回结果
    message = {'code': 100003, 'msg': '权限错误'}

    # 结合源码解释
    def has_permission(self, request, view):
        # 获取请求中的数据,然后进行校验,先了解,下面会介绍用法
        print('MyPermission1')
        role = 1
        if role == 1:
            return True
        return False


class MyPermission2(BasePermission):

    # 配置接口没有权限时,返回结果
    message = {'code': 100003, 'msg': '权限错误'}

    # 结合源码解释
    def has_permission(self, request, view):
        # 获取请求中的数据,然后进行校验,先了解,下面会介绍用法
        print('MyPermission2')
        role = 1
        if role == 2:
            return True
        return False


class MyPermission3(BasePermission):

    # 配置接口没有权限时,返回结果
    message = {'code': 100003, 'msg': '权限错误'}

    # 结合源码解释
    def has_permission(self, request, view):
        # 获取请求中的数据,然后进行校验,先了解,下面会介绍用法
        print('MyPermission3')
        role = 1
        if role == 1:
            return True
        return False

7.1.1,应用 三个权限

# 导入权限类
from .ext.MyPermission import MyPermission1, MyPermission2, MyPermission3


# 修改认证类创建的UserView, 添加权限
class UserView(APIView):

    # permission_classes 为权限类的默认配置参数,必须怎么写,详细过程可以查看源码
    # 当不使用权限类时,列表为空
    # 请注意,当类中使用permission_classes=[权限类1, 权限类2, ...]时,权限默认使用就近原则,使用类中的permission_classes
    permission_classes = [MyPermission1, MyPermission2, MyPermission3]

    def get(self, request, *args, **kwargs):
        return Response('GET')

7.1.2,控制台输出

 7.1.3,从控制台可以看出,只输出了MyPermission1和MyPermission2,MyPermission3并没有触发,因此,权限组件默认情况下:当所有的权限类全部为True时,才通过权限

7.2、通过源码可以看出判断关系是在check_permissions中进行验证

    # 权限循环判断方法,源码位于rest_framework/views中
    def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        # get_permissions对象来自配置配置文件中的DEFAULT_PERMISSION_CLASSES
        for permission in self.get_permissions():
            # has_permission来自定义权限类中,权限类中继承与BasePermission,硬性要求,权限必须实现此方法
            # 当has_permission返回True时,not True为False,不会运行if判断,相反同理
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request,
                    # 通过反射找到配置的message,没有返回None
                    message=getattr(permission, 'message', None),
                    # 我们定义了message,code就不需要定义,当然,想使用了可以定制
                    code=getattr(permission, 'code', None)
                )

7.3、此方法来源于APIView,当修改源码时,不能直接修改DRF的源码,但是,我们可以在继承APIView的基础上重写此方法

7.3.1、在ext中新建myAPIView.py,并添加自己的APIView类

# 导入APIView
from rest_framework.views import APIView


# 继承APIView
class MyAPIView(APIView):

    # 重写此方法
    def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        # 定义空列表
        permissions = []
        for permission in self.get_permissions():
            # 当has_permission为True时,返回空,返回空时,运行下一个认证
            if permission.has_permission(request, self):
                return
            else:
                # 当has_permission为False时,放入列表中
                permissions.append(permission)
        # 当有认证失败时,进行if判断
        if permissions:
            self.permission_denied(
                request,
                # 因为定义的认证失败都是一致的,所有异常时只取一个错误及permissions[0]
                message=getattr(permissions[0], 'message', None),
                code=getattr(permissions[0], 'code', None)
            )

7.3.2、修改 UserView,使其继承 MyAPIView,并在此访问接口

# 导入认证类
from .ext.MyPermission import MyPermission1, MyPermission2, MyPermission3


# 修改认证类创建的UserView, 添加权限
class UserView(MyAPIView):

    # permission_classes 为权限类的默认配置参数,必须怎么写,详细过程可以查看源码
    # 当不使用权限类时,列表为空
    # 请注意,当类中使用permission_classes=[权限类1, 权限类2, ...]时,权限默认使用就近原则,使用类中的permission_classes
    permission_classes = [MyPermission1, MyPermission2, MyPermission3]

    def get(self, request, *args, **kwargs):
        return Response('GET')

# postman访问接口,接口测试成功
# 返回get

控制台输出

 至此,或关系修改成功

 7.4、结合认证使用权限

7.4.1、通过源码可以看出权限是在认证后进行的,源码如下:

    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)

7.4.2、在学习认证时,通过4.1的源码可以看到,认证通过后,对user和admin进行了赋值,及self.user, self.auth

def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        # 认证类源码
        for authenticator in self.authenticators:
            try:
                # 循环认证类
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise
            # 当认证类返回不为空时,赋值user,auth和authenticator
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

 7.4.4,修改权限代码

# 导入权限rest_framework的权限类
from rest_framework.permissions import BasePermission


class MyPermission1(BasePermission):
    message = {'code': 100003, 'msg': '权限错误'}

    def has_permission(self, request, view):
        print('MyPermission1')
        # 认证时,对self.user了赋值,实际上是对request中的user进行赋值
        # 赋值成功后,self.user为当前数据库表的实例
        # 通过实例可以调用相关的字段数据
        role = request.user.role
        if role == 1:
            return True
        return False


class MyPermission2(BasePermission):
    message = {'code': 100003, 'msg': '权限错误'}

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


class MyPermission3(BasePermission):
    message = {'code': 100003, 'msg': '权限错误'}

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

7.4.5、数据库添加role数据

7.4.6、修改视图函数

# 导入认证类
from .ext.MyPermission import MyPermission1, MyPermission2, MyPermission3


class LoginView(MyAPIView):

    # authentication_classes 为认证类的默认配置参数,必须怎么写,详细过程可以查看源码
    # 当不使用认证类时,列表为空
    # 请注意,当类中使用authentication_classes=[认证类1, 认证类2, ...]时,认证默认使用就近原则,使用类中的authentication_classes
    authentication_classes = [MyPermission1]  # role=1的权限

    def get(self, request, *args, **kwargs):
        return Response('GET')


# 修改认证类创建的UserView, 添加权限
class UserView(MyAPIView):

    # permission_classes 为权限类的默认配置参数,必须怎么写,详细过程可以查看源码
    # 当不使用权限类时,列表为空
    # 请注意,当类中使用permission_classes=[权限类1, 权限类2, ...]时,权限默认使用就近原则,使用类中的permission_classes
    permission_classes = [MyPermission1, MyPermission2]  # role=1和2的的权限

    def get(self, request, *args, **kwargs):
        # self.dispatch()
        return Response('GET')


class OrderView(MyAPIView):

    permission_classes = [MyPermission2, MyPermission3]  # role=2和3的的权限

    def get(self, request, *args, **kwargs):
        return Response('GET')


# 接口访问结果:
# 1、返回 LoginView 接口,pass
# 2、访问 UserView 接口,pass
# 3、访问 OrderView 接口,返回
# {
#     "code": "100003",
#     "msg": "权限错误"
# }

总结:

1、因为权限的特殊性,最好不要配置全局参数,应用时,在接口中使用;

2、默认权限的运行过程是 and 的 关系,对于分开权限认证不支持,最好重写成 or 的关系;

3、实际应用中,权限和认证是结合使用的,如果想分开使用,可以重写相关方法,但不建议分开使用;

4、请注意:认证源码通过view中的initialize_request方法进行查看,而权限是在initial的方法中。

 

posted @ 2024-12-26 18:47  蜗牛·哥  阅读(42)  评论(0)    收藏  举报