认证组件 = [认证类,认证类,认证类,] -> 执行每一个认证类的authenticate方法
认证成功或失败,不会再执行后续的认证类
返回None,执行后续认证类
权限组件 = [权限类,权限类,权限类,] -> 执行所有权限类的has_permission方法,返回True通过,返回False不通过
- 执行所有的权限类
默认情况下,要保证所有的权限类中的has_permission方法都返回True,即
与
的关系掌握源码流程后,可以扩展+自定义,实现其他逻辑
1. 快速使用
- 编写类
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
阅读源码可知,自定义的权限类的has_permission
应返回True或False
来表示是否有权限
# ext/
from rest_framework.permissions import BasePermission
import random
class MyPermission(BasePermission):
def has_permission(self, request, view):
# 获取请求中的数据,然后进行校验...
v1 = random.randint(1, 3)
if v1 == 2:
return True
return False
- 应用类
全局应用 在
settings.py
中配置局部应用 在视图函数中定义
permission_classes = []
参数根据业务需求灵活配置
2. 错误信息和多个权限类
- 在权限类中自定义错误信息
message
class MyPermission(BasePermission):
message = {"status": False, "msg": "无权访问"}
def has_permission(self, request, view):
# 获取请求中的数据,然后进行校验...
v1 = random.randint(1, 3)
if v1 == 2:
return True
return False
左图为默认错误信息,右图为自定义错误信息
- 多个权限类
api/per.py
from rest_framework.permissions import BasePermission
class MyPermission1(BasePermission):
message = {"status": False, "msg": "无权访问"}
def has_permission(self, request, view):
print("MyPermission1")
return False
class MyPermission2(BasePermission):
message = {"status": False, "msg": "无权访问"}
def has_permission(self, request, view):
print("MyPermission2")
return False
class MyPermission3(BasePermission):
message = {"status": False, "msg": "无权访问"}
def has_permission(self, request, view):
print("MyPermission3")
return False
api/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from ext.per import MyPermission1, MyPermission2, MyPermission3
class OrderView(APIView):
authentication_classes = []
permission_classes = [MyPermission1, MyPermission2, MyPermission3]
def get(self, request):
print(request.user, request.auth)
return Response({"status": True, "data": [11, 22, 33, 44]})
终端只打印了MyPermission1
说明程序会按顺序校验每个权限类,一旦有一个返回False即校验不通过,则不再校验后续权限类,只有通过每一个权限类的校验才算有权限访问
3. 源码流程
as_view()等方法的调用在认证组件中已详细阐明,此处直接从dispatch()方法开始
class APIView(View):
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES # 权限组件的全局配置
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)
def get_permissions(self):
# 获取权限类对象的列表
return [permission() for permission in self.permission_classes]
def check_permissions(self, request):
# 循环每一个权限类对象,判断权限校验是否通过
for permission in self.get_permissions():
# 如果未通过,则抛出异常在dispatch中捕获
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
def initial(self, request, *args, **kwargs):
self.perform_authentication(request) # 认证组件的过程,循环执行每个authenticate方法;失败则抛出异常不向下执行
self.check_permissions(request) # 权限的校验
self.check_throttles(request)
def dispatch(self, request, *args, **kwargs):
# 封装drf的request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# 反射获取get/post等方法
handler = getattr(self, request.method.lower(), 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
class OrderView(APIView):
permission_classes = [MyPermission1, MyPermission2, MyPermission3] # 权限组件的局部配置
def get(self, request):
print(request.user, request.auth)
return Response({"status": True, "data": [11, 22, 33, 44]})
4. 组件的拓展
通过源码可以知道,在
check_permissions
中,只要有一个权限类校验不通过就是无权访问,那么如何才能实现拓展,使其满足只要有一个权限类通过就是有权限呢?显而易见需要重写check_permissions
方法
class OrderView(APIView):
authentication_classes = []
permission_classes = [MyPermission1, MyPermission2, MyPermission3]
def get(self, request):
print(request.user, request.auth)
return Response({"status": True, "data": [11, 22, 33, 44]})
def check_permissions(self, request):
no_permission_objects_list = []
for permission in self.get_permissions():
if permission.has_permission(request, self):
return
else:
no_permission_objects_list.append(permission)
else:
self.permission_denied(
request,
message=getattr(no_permission_objects_list[0], 'message', None),
code=getattr(no_permission_objects_list[0], 'code', None)
)
在
OrderView
视图中重写check_permissions
方法,则在权限校验的生命周期中,自定义的check_permissions
便覆盖了APIView
中的默认check_permissions
实现逻辑,达到自定义拓展的效果自定义的
check_permissions
实现了一旦某一权限类校验通过,就不再进行后续校验。若所有类均未通过,则返回第一个未通过的类的错误信息由此可见学习源码的重要性,不仅可以学习源码中优秀的代码编写方式,还可以在原有逻辑的基础上进行拓展
上述是修改一个视图函数的权限校验逻辑,如何应用到其他视图呢?
可以自定义一个
NbApiView
的类,继承APIView
并且重写check_permissions
,之后要实现或逻辑的权限认证的视图只需继承NbApiView
即可
ext/view.py
from rest_framework.views import APIView
class NbApiView(APIView):
def check_permissions(self, request):
no_permission_objects_list = []
for permission in self.get_permissions():
if permission.has_permission(request, self):
return
else:
no_permission_objects_list.append(permission)
else:
self.permission_denied(
request,
message=getattr(no_permission_objects_list[0], 'message', None),
code=getattr(no_permission_objects_list[0], 'code', None)
)
- api/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from ext.per import MyPermission1, MyPermission2, MyPermission3
from ext.view import NbApiView
class OrderView(NbApiView):
authentication_classes = []
permission_classes = [MyPermission1, MyPermission2, MyPermission3]
def get(self, request):
print(request.user, request.auth)
return Response({"status": True, "data": [11, 22, 33, 44]})