Django Rest Framework自定义权限不起作用



我希望用户只能访问属于他们的记录,而不能访问任何其他用户的记录,因此我创建了以下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)

我只希望记录的所有者能够访问该视图中的所有方法,即检索、列表等(稍后我将实现其余方法(,因此我在core应用程序中创建了以下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

这不起作用,所以在查看了stackoverflow的答案后,我发现这是Django Rest Framework的所有者权限,它指示必须实现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

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

这个答案对我来说是正确的。

上面写着:

列表视图不调用has_object_permission。这个文件显示:

还要注意,通用视图将只检查检索单个模型实例的视图的对象级别权限。如果你需要列表视图的对象级筛选,则需要进行筛选单独设置查询集。有关详细信息,请参阅筛选文档详细信息。

文档链接

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

您还需要编写has_permission,以便使您的自定义权限发挥作用。

这是官方文档并提到了它。它应该在您添加has_permission后工作。

如文档中所述,对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()

最新更新