【问题标题】:Django Rest Framework custom permission not workingDjango Rest Framework 自定义权限不起作用
【发布时间】:2021-06-18 16:38:22
【问题描述】:

我希望用户只能访问属于他们的记录,而不是任何其他用户的记录,所以 我创建了以下view

class AddressViewSet(viewsets.ModelViewSet):

    authentication_classes = (TokenAuthentication,)
    permission_classes = [IsAuthenticated, IsOwner]
    queryset = Address.objects.all()

    def retrieve(self, request, pk):
        address = self.address_service.get_by_id(pk)
        serializer = AddressSerializer(address)
        return Response(serializer.data, status=status.HTTP_200_OK)

我希望只有记录的所有者才能访问此视图中的所有方法,即检索、列表等(稍后我将实现其余方法),因此我在 @987654325 中创建了以下 permissions.py 文件@应用:

class IsOwner(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        print('here in has_object_permission...')
        return obj.user == request.user

这不起作用,所以在通过*答案后,我发现了这个Django Rest Framework owner permissions,它表明必须实现has_permission方法。但正如您在该答案中看到的那样,它试图从view.kwargs 中获取id,但我的view.kwargs 只包含pk 而不是用户。我怎样才能解决这个问题?我是否需要在请求 url 中隐式传递用户 ID?听起来不对。

这是我用来验证用户无法访问其他用户记录的测试:

def test_when_a_user_tries_to_access_another_users_address_then_an_error_is_returned(self):
        user2 = UserFactory.create()
        addresses = AddressFactory.create_batch(3, user=user2)
        address_ids = [address.id for address in addresses]
        random_address_id = random.choice(address_ids)

        url = reverse(self.ADDRESSES_DETAIL_URL, args=(random_address_id,))

        res = self.client.get(url, format='json')

        print(res.data)

目前只是使用测试来检查返回的数据,稍后会实现断言。

编辑

所以我在IsOwner 中添加了has_permission 方法:

def has_permission(self, request, view):
        return request.user and request.user.is_authenticated

如果我在这里放置一个打印语句,它会被打印,但似乎没有达到has_object_permission 方法,我在那里添加的所有打印都不会显示

【问题讨论】:

    标签: django django-rest-framework django-rest-auth django-permissions django-rest-viewsets


    【解决方案1】:

    This answer 适合我。

    上面写着:

    列表视图不调用 has_object_permission。这 文档说明如下:

    另请注意,通用视图只会检查检索单个模型实例的视图的对象级权限。如果你 需要对列表视图进行对象级过滤,您需要过滤 单独的查询集。有关更多信息,请参阅过滤文档 详情。

    Link to documentation

    【讨论】:

      【解决方案2】:

      注意:实例级别的 has_object_permission 方法只有在视图级别的 has_permission 检查已经通过时才会被调用。

      您还需要编写 has_permission 以使您的自定义权限有效。

      Here 是官方文档并提到它。添加 has_permission 后它应该可以工作。

      【讨论】:

      • 在我的编辑中,您可以看到我添加了它,但从未调用过 has_object_permission。通过在两种方法中添加print 语句,我可以看到has_permission 被调用但has_object_permission 不是
      • 如果可以的话,请告诉我您的完整权限代码
      【解决方案3】:

      the docs 中所述,在self.get_object 方法调用上检查权限。

      def get_object(self):
          obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"])
          self.check_object_permissions(self.request, obj)
          return obj
      

      这基本上是retrieve 方法在ModelViewSet 中所做的全部

          def retrieve(self, request, *args, **kwargs):
              instance = self.get_object()
              serializer = self.get_serializer(instance)
              return Response(serializer.data)
      

      无论你在self.address_service.get_by_id(pk) 中做什么,都应该移动到self.get_object 或在retrieve 方法中调用self.check_object_permissions(self.request, obj)

      在基本场景中,这就是您所需要的。无需覆盖retrieve 方法。

      class AddressViewSet(viewsets.ModelViewSet):
          serializer_class = AddressSerializer
          authentication_classes = (TokenAuthentication,)
          permission_classes = [IsAuthenticated, IsOwner]
          queryset = Address.objects.all()
      

      【讨论】:

        最近更新 更多