DRF权限
DRF 的权限系统用于控制用户对 API 端点的访问权限,它在认证系统之后执行,决定已认证用户可以执行什么操作。
1、BasePermission
所有权限类都应从其继承的基类。
class BasePermission(metaclass=BasePermissionMetaclass):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return True
2、内置的权限类
2.1 AllowAny
允许任何用户访问,无论是否认证。
class AllowAny(BasePermission):
def has_permission(self, request, view):
return True
2.2 IsAuthenticated
只允许已认证的用户访问。
class IsAuthenticated(BasePermission):
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
2.3 IsAdminUser
只允许管理员用户访问(user.is_staff = True)
class IsAdminUser(BasePermission):
def has_permission(self, request, view):
return bool(request.user and request.user.is_staff)
2.4 IsAuthenticatedOrReadOnly
已认证用户或只读请求
SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
class IsAuthenticatedOrReadOnly(BasePermission):
def has_permission(self, request, view):
return bool(
request.method in SAFE_METHODS or
request.user and
request.user.is_authenticated
)
2.5 DjangoModelPermissions
基于 Django 的模型权限系统,自动映射到 DRF 的 HTTP 方法,映射关系如下:
perms_map = {
'GET': [],
'OPTIONS': [],
'HEAD': [],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
class DjangoModelPermissions(Baseermission):
authenticated_users_only = True
def has_permission(self, request, view):
if not request.user or (
not request.user.is_authenticated and self.authenticated_users_only):
return False
queryset = self._queryset(view)
perms = self.get_required_permissions(request.method, queryset.model)
return request.user.has_perms(perms)
2.6 DjangoModelPermissionsOrAnonReadOnly
类似于 DjangoModelPermissions,但允许匿名用户进行只读访问。
authenticated_users_only = False
2.7 DjangoObjectPermissions
基于 Django 的对象级权限,需要 django-guardian 包支持
class DjangoObjectPermissions(DjangoModelPermissions):
def has_object_permission(self, request, view, obj):
queryset = self._queryset(view)
model_cls = queryset.model
user = request.user
perms = self.get_required_object_permissions(request.method, model_cls)
if not user.has_perms(perms, obj):
if request.method in SAFE_METHODS:
raise Http404
read_perms = self.get_required_object_permissions('GET', model_cls)
if not user.has_perms(read_perms, obj):
raise Http404
return False
return True
3、自定义权限类
创建自定义权限类需要继承 BasePermission 并实现 has_permission 和/或 has_object_permission 方法。
from rest_framework.permissions import BasePermission
class IsOwner(BasePermission):
"""
自定义权限:只允许对象的所有者访问
"""
def has_object_permission(self, request, view, obj):
# 检查用户是否是对象的所有者
return obj.owner == request.user
class IsOwnerOrReadOnly(BasePermission):
"""
自定义权限:允许所有人读取,但只有所有者可以修改
"""
def has_object_permission(self, request, view, obj):
# 允许安全的HTTP方法(GET, HEAD, OPTIONS)
if request.method in permissions.SAFE_METHODS:
return True
# 写操作只允许所有者
return obj.owner == request.user
4、权限使用
4.1 全局权限配置
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
4.2 视图级权限配置
# 类视图
class MyView(APIView):
permission_classes = [IsAuthenticated, IsOwner]
# 视图集
class MyViewSet(ModelViewSet):
permission_classes = [IsAuthenticated, BusinessHoursOnly]
# 函数视图
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def my_view(request):
return Response({"message": "Authenticated user only"})
5、DRF权限校验源码
APIView.dispatch->initial->check_permissions->
class APIView(View):
def dispatch(self, request, *args, **kwargs):
self.initial(request, *args,
def initial(self, request, *args, **kwargs):
self.check_permissions(request)
def check_permissions(self, request):
# 循环调用每个权限类的has_permission方法,任何一个返回False则抛出异常
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)
)
# 该方法有被GenericAPIView中的get_object调用
def check_object_permissions(self, request, obj):
# 循环调用每个权限类的has_object_permission方法,任何一个返回False则抛出异常
for permission in self.get_permissions():
if not permission.has_object_permission(request, self, obj):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)