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"],
}
源码分析
源码详细逐步分析
①、请求进来是执行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
②、进入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)
③、进入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方法
⑥、进入到如果有这个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'})