REST framework 权限管理源码分析
同认证一样,dispatch()作为入口,从self.initial(request, *args, **kwargs)
进入initial()
def initial(self, request, *args, **kwargs):# .......# 用户认证self.perform_authentication(request)# 权限控制self.check_permissions(request)# ...
check_permissions()
便是权限管理源码的入口
# 权限管理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))
和用户认证一样,同样遍历一个权限类对象列表,并且调用该列表中元素的has_permission()
方法,该方法返回布尔值,True
代表有权限,False
代表没有权限.
def get_permissions(self):return [permission() for permission in self.permission_classes]
如果没有权限,就调用permission_denied()
def permission_denied(self, request, message=None):if request.authenticators and not request.successful_authenticator:raise exceptions.NotAuthenticated()raise exceptions.PermissionDenied(detail=message)
如果使用了REST的认证框架(authentication_classes数组不为空)并且身份认证失败,就抛出NotAuthenticated异常,否则会抛出PermissionDenied异常
class NotAuthenticated(APIException):status_code = status.HTTP_401_UNAUTHORIZEDdefault_detail = _('Authentication credentials were not provided.')default_code = 'not_authenticated'
NotAuthenticated会导致一个401错误(缺少用户凭证)
class PermissionDenied(APIException):status_code = status.HTTP_403_FORBIDDENdefault_detail = _('You do not have permission to perform this action.')default_code = 'permission_denied'
而PermissionDenied会返回错误403(拒绝授权访问)
在向permission_denied()
类传递参数时,使用了反射
self.permission_denied(request, message=getattr(permission, 'message', None))
会在这个权限类对象中寻找message
属性,没找到就使用None
,而这个参数在后来只会被用在PermissionDenied异常上,这些异常都继承自APIException,而在APIException的构造器中,可以发现detail参数就是异常描述,而在自己的权限类中定义message属性可以改变认证失败后的描述
class APIException(Exception):status_code = status.HTTP_500_INTERNAL_SERVER_ERRORdefault_detail = _('A server error occurred.')default_code = 'error'def __init__(self, detail=None, code=None):if detail is None:detail = self.default_detailif code is None:code = self.default_code# ...
示例
# api/utils/Permission.py
from rest_framework.permissions import BasePermissionclass CommonPermission(BasePermission):"""普通用户权限,作用于全局"""def has_permission(self, request, view):print(request.user)if request.user.user_type == 1:return Truedef has_object_permission(self, request, view, obj):"""一旦获得View权限,将获得所有object权限:return: True"""return Trueclass VipPermission(BasePermission):"""VIP 用户权限"""message = '您首先要称为VIP才能访问'def has_permission(self, request, view):print(request.user)if request.user.user_type == 2:return Truedef has_object_permission(self, request, view, obj):return True
# api/view.py
from django.shortcuts import HttpResponse
from django.http import JsonResponse
from rest_framework.views import APIView
from api.utils.Permission import VipPermissionclass LoginView(APIView):authentication_classes = []# 登录无需权限认证permission_classes = []def post(self, request, *args, **kwargs):pass@method_decorator(csrf_exempt, name='dispatch')
class ShopView(APIView):def get(self, request, *args, **kwargs):return HttpResponse(request.user.username)def post(self, request, *args, **kwargs):return HttpResponse('POST')class VipIndexView(APIView):"""只授权给VIP用户查看"""permission_classes = [VipPermission]def get(self, *args, **kwargs):return JsonResponse("welcome VIP ", safe=False)
# RESTdemo.setting.py
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.MyAuthentication.MyAuthentication'],'UNAUTHENTICATED_USER': None,'UNAUTHENTICATED_TOKEN': None,'DEFAULT_PERMISSION_CLASSES': ['api.utils.Permission.CommonPermission']
}