Django REST Framework多权限类组合校验不生效问题
问题根因
- 权限类逻辑覆盖不全:
IsAuthorGroup仅实现了全局级的has_permission校验,没有重写对象级的has_object_permission,父类BasePermission默认直接返回TrueIsOwnerOrReadOnly仅实现了对象级校验,没有重写全局级has_permission,父类默认直接返回TruePOST是集合级创建操作,执行时还没有生成持久化的模型对象,根本不会触发has_object_permission校验,原有的所有权校验逻辑完全管不到POST请求
- 业务逻辑漏配:
BlogSerializer把author字段加入了exclude列表,创建博客时没有强制把作者绑定为当前请求用户,可能出现创建出的博客作者为空、或被恶意篡改的情况 - 代码细节不严谨:判断用户是否在组时直接用QuerySet做布尔判断查询效率低;判断用户状态未显式校验登录态,匿名用户场景下存在逻辑隐患
修复方案
1. 重写权限类,补全全局+对象级校验逻辑
直接整合权限逻辑,避免复合权限组合出现不可预期的问题:
from rest_framework import permissions class BlogPermission(permissions.BasePermission): def has_permission(self, request, view): # 所有请求的基础准入条件:登录状态+属于AuthorGroup组 if not (request.user and request.user.is_authenticated and request.user.groups.filter(name='AuthorGroup').exists()): return False # 读请求、POST创建请求直接放行,POST的作者绑定在视图层处理 if request.method in permissions.SAFE_METHODS or request.method == 'POST': return True # 其余单对象写操作(PUT/PATCH/DELETE)留到对象级校验所有权 return True def has_object_permission(self, request, view, obj): # 单对象读请求组内用户均可访问 if request.method in permissions.SAFE_METHODS: return True # 写操作仅允许作者本人或超管操作 return obj.author == request.user or request.user.is_superuser
2. 修改视图配置,补全创建时的作者绑定
替换视图的权限类,重写perform_create方法强制绑定作者,避免前端篡改作者字段:
class BlogViewSet(viewsets.ModelViewSet): queryset = Blog.objects.all() serializer_class = BlogSerializer pagination_class = BlogPagination lookup_field = 'blog_slug' # 替换为新的权限类,无需再用&组合 permission_classes = [BlogPermission] def perform_create(self, serializer): # 创建博客时强制将作者设为当前登录用户 serializer.save(author=self.request.user)
(可选)保留独立权限类的正确写法
如果需要保留两个独立权限类复用,要给每个类补全全局、对象级的校验方法,再组合使用:
class IsAuthorGroup(permissions.BasePermission): def has_permission(self, request, view): # 用exists()提升组查询效率,显式校验登录态 return bool( request.user and request.user.is_authenticated and request.user.groups.filter(name='AuthorGroup').exists() ) def has_object_permission(self, request, view, obj): # 对象级校验复用全局组校验逻辑,不要默认返回True return self.has_permission(request, view) class IsOwnerOrReadOnly(permissions.BasePermission): def has_permission(self, request, view): # 读请求、POST创建请求全局放行,其余写操作留到对象级校验 return True def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True return obj.author == request.user or request.user.is_superuser
这种写法下原有permission_classes = [IsOwnerOrReadOnly & IsAuthorGroup]配置即可正常生效。
最终权限逻辑
- 所有访问博客接口的用户必须是登录状态的AuthorGroup组成员,非组成员直接无权限
- 组内用户都可以浏览博客内容、发布新博客,发布的博客自动绑定当前用户为作者
- 只有博客作者、超级管理员可以编辑/删除对应博客,组内其他用户没有操作权限
- 不存在“非所有者发POST”的逻辑漏洞:POST是创建新博客,创建后当前用户就是该博客的所有者,完全符合权限要求
内容的提问来源于stack exchange,提问作者boyenec
相关产品推荐
相关产品推荐

