三、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的方法中。

浙公网安备 33010602011771号