You need to enable JavaScript to run this app. DRF函数视图下Token认证无法触发has_object_permission的原因与实现
优惠活动
大模型
产品
解决方案
定价
更多

DRF函数视图下Token认证无法触发has_object_permission的原因与实现

核心原因解析
  • DRF的权限机制存在明确分层,不同层级的触发逻辑完全不同:
    • has_permission 属于全局级权限校验,不需要绑定具体业务对象就能判断(比如判断请求用户是否登录、是否为管理员)。这部分逻辑会被@api_view装饰器、通用类视图在请求入口自动触发执行,和使用哪种认证方式没有关联。
    • has_object_permission 属于对象级权限校验,必须拿到具体的业务实例才能判断(比如判断当前用户是不是目标训练记录的所有者)。框架从来不会自动触发这个方法的执行。
  • 通用类视图(如你实现的TrainingDetail(generics.RetrieveUpdateAPIView))能自动触发对象权限,是因为它的内部get_object()方法有固定流程:先从数据库查出目标实例,紧接着调用self.check_object_permissions(request, obj),遍历所有配置的权限类执行has_object_permission校验。这个流程是内置的,不管用Session还是Token认证,只要请求走到取对象的逻辑,权限检查就会触发,因此切换为类视图后一切表现正常。
  • 纯函数式视图没有内置的get_object()固定流程,框架只会在视图入口帮你跑完has_permission检查,完全不知道你什么时候通过ORM拿到了要操作的具体对象,更不会主动帮你调用对象权限的检查。
    你之前误以为Session场景下函数视图的权限能正常工作,本质是两个场景带来的认知偏差:
    1. 当时的测试请求以GET为主,而你写的自定义权限里,GET这类安全请求直接返回True,就算权限被触发也不会产生拦截效果,根本暴露不出缺失校验的问题。
    2. 用Postman发Token认证的PUT/DELETE请求时,因为缺少对象权限的手动触发逻辑,框架只校验了“用户是否登录”(即IsAuthenticated权限),根本没走到所有者校验的逻辑,自然就能跨用户修改数据,也不会输出对应的权限打印日志。
函数视图的正确适配方式

在函数视图中,当你通过ORM取出目标training对象之后,必须手动调用DRF提供的对象权限检查方法,补全缺失的校验逻辑,即可同时适配Session和Token两种认证流程:

@api_view(['GET', 'PUT', 'DELETE'])
@permission_classes([permissions.IsAuthenticated, IsTrainingOwner])
def training_detail(request, pk, format=None):
    """
    Retrieve, update or delete a training. Need to be authenticated
    """
    print(request.user)
    try:
        training = Training.objects.get(pk=pk)
    except Training.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # 从请求上下文拿到被装饰器包装的视图实例,手动触发对象权限校验
    view = request.parser_context['view']
    view.check_object_permissions(request, training)

    if request.method == 'GET':
        serializer = TrainingSerializer(training)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = TrainingSerializer(training, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        training.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
  • 上述调用的check_object_permissions方法,和类视图内部get_object()中的校验逻辑完全等价:遍历你配置的所有权限类,挨个调用对应的has_object_permission方法,只要任意权限不通过,就自动返回403响应。
  • 补完这步逻辑后,不管是Session认证的浏览API请求,还是Token认证的Postman请求,都会正常触发IsTrainingOwner的权限检查:PUT/DELETE请求会校验当前用户是否为训练记录的所有者,跨用户操作会直接被拦截,控制台也能输出对应的权限打印信息。

内容的提问来源于stack exchange,提问作者DrSalas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 04:15:04