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场景下函数视图的权限能正常工作,本质是两个场景带来的认知偏差:- 当时的测试请求以GET为主,而你写的自定义权限里,GET这类安全请求直接返回True,就算权限被触发也不会产生拦截效果,根本暴露不出缺失校验的问题。
- 用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
相关产品推荐
相关产品推荐

