You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django REST Framework多权限类组合校验不生效问题

问题根因
  • 权限类逻辑覆盖不全:
    1. IsAuthorGroup仅实现了全局级的has_permission校验,没有重写对象级的has_object_permission,父类BasePermission默认直接返回True
    2. IsOwnerOrReadOnly仅实现了对象级校验,没有重写全局级has_permission,父类默认直接返回True
    3. POST是集合级创建操作,执行时还没有生成持久化的模型对象,根本不会触发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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 04:45:38