Django REST框架对象级权限

use*_*568 24 python django permissions django-rest-framework

我正在使用Django REST Framework来访问资源"用户".

由于用户信息是个人信息,我不希望GET请求列出系统中的每个用户,除非他们是管理员.

如果用户指定了他们的id,并且他们已登录,我希望他们能够查看他们的详细信息并在需要时修改它们(PUT POST DELETE).

总而言之,对于不是管理员的人,请禁用GET方法,并在查看其信息时允许登录用户使用GET POST DELETE PUT.

所以我创建了自定义权限类:

class UserPermissions(permissions.BasePermission):
    """
    Owners of the object or admins can do anything.
    Everyone else can do nothing.
"""

    def has_permission(self, request, view):
        # if admin: True otherwise False
    def has_object_permission(self, request, view, obj):
        # if request.user is the same user that is contained within the obj then allow
Run Code Online (Sandbox Code Playgroud)

这没用.经过一些调试后,我发现它首先检查has_permission,然后检查has_object_permission.因此,如果我们没有超过第一个障碍GET/user /,那么它甚至不会考虑下一个GET/user/id.

所以总之,有谁知道如何让这个工作?

谢谢 :)

编辑:

我正在使用ModelViewSets,它确实有我所描述的这个问题.

但是,如果使用Detail拆分List功能,则可以为它们分配不同的权限类:

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes=(UserPermissionsAll,)

class UserDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes=(UserPermissionsObj,)

class UserPermissionsAll(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""

    def has_permission(self, request, view):
        if request.user.is_staff:
            return True
        else:
            return False

class UserPermissionsObj(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""

    def has_object_permission(self, request, view, obj):
        if request.user.is_staff:
            return True

        return obj == request.user
Run Code Online (Sandbox Code Playgroud)

wil*_*art 20

我过去使用自定义权限执行此操作并覆盖has_object_permission如下所示:

from rest_framework import permissions


class MyUserPermissions(permissions.BasePermission):
    """
    Handles permissions for users.  The basic rules are

     - owner may GET, PUT, POST, DELETE
     - nobody else can access
     """

    def has_object_permission(self, request, view, obj):

        # check if user is owner
        return request.user == obj
Run Code Online (Sandbox Code Playgroud)

您可以执行一些更详细的操作,例如拒绝特定的请求类型(例如,允许对所有用户的GET请求):

class MyUserPermissions(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):

        # Allow get requests for all
        if request.method == 'GET':
            return True
        return request.user == obj
Run Code Online (Sandbox Code Playgroud)

然后在您的视图中,您告诉它使用权限类:

from my_custom_permissions import MyUserPermissions

class UserView(generics.ListCreateAPIView):
    ...
    permission_classes = (MyUserPermissions, )
    ...
Run Code Online (Sandbox Code Playgroud)


Tim*_*ick 12

我有类似的需求.让我们称之为我的应用程序x.这就是我想出来的.

首先,把它放在x/viewsets.py:

# viewsets.py
from rest_framework import mixins, viewsets

class DetailViewSet(
  mixins.CreateModelMixin,
  mixins.RetrieveModelMixin,
  mixins.UpdateModelMixin,
  mixins.DestroyModelMixin,
  viewsets.GenericViewSet):
    pass

class ReadOnlyDetailViewSet(
  mixins.RetrieveModelMixin,
  viewsets.GenericViewSet):
    pass

class ListViewSet(
  mixins.ListModelMixin,
  viewsets.GenericViewSet):
    pass
Run Code Online (Sandbox Code Playgroud)

然后在x/permissions.py:

# permissions.py
from rest_framework import permissions

class UserIsOwnerOrAdmin(permissions.BasePermission):
    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated()

    def check_object_permission(self, user, obj):
        return (user and user.is_authenticated() and
          (user.is_staff or obj == user))

    def has_object_permission(self, request, view, obj):
        return self.check_object_permission(request.user, obj)
Run Code Online (Sandbox Code Playgroud)

然后在x/views.py:

# views.py
from x.viewsets import DetailViewSet, ListViewSet
from rest_framework import permissions

class UserDetailViewSet(DetailViewSet):
    queryset = User.objects.all()
    serializer_class = UserDetailSerializer
    permission_classes = (UserIsOwnerOrAdmin,)

class UserViewSet(ListViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes (permissions.IsAdminUser,)
Run Code Online (Sandbox Code Playgroud)

顺便说一下,请注意您可以为这两个视图集使用不同的序列化程序,这意味着您可以在list视图中显示与视图中不同的属性retrieve!例如:

# serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'url',)

class UserDetailSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'groups', 'profile', 'password',)
        write_only_fields = ('password',)
Run Code Online (Sandbox Code Playgroud)

然后在x/urls.py:

# urls.py
from x import views
from rest_framework import routers

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'users', views.UserDetailViewSet)

...
Run Code Online (Sandbox Code Playgroud)

我有点意外地router接受了相同的模式两次,但它似乎确实有效.

警告:我已通过API浏览器确认这一切都有效,但我还没有尝试通过API进行更新.


ken*_*eni 9

对于绊脚石,对象级别权限限制下的文档说:

For performance reasons the generic views will not automatically apply object level permissions to each instance in a queryset when returning a list of objects.
Run Code Online (Sandbox Code Playgroud)

因此,详细信息视图将起作用,但对于列表,您需要针对当前用户进行过滤.

  • [`django-rest-framework-guardian`](https://github.com/rpkilby/django-rest-framework-guardian) 是帮助自动过滤的选项之一,如果您正在使用 [`django-guardian`](https ://github.com/django-guardian/django-guardian)。 (2认同)

Che*_*mer 7

对于@ will-hart的回答还有一件事.

在DRF3文档中,

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

因此,has_permission应指定使用has_object_permission.

from rest_framework import permissions

class MyUserPermissions(permissions.BasePermission):

    def has_permission(self, request, view):
        return True

    def has_object_permission(self, request, view, obj):
        return request.user == obj
Run Code Online (Sandbox Code Playgroud)

但是,当用户尝试获取用户列表时,上述代码将授予任何人权限.在这种情况下,最好是根据而action不是给予许可HTTP method.

from rest_framework import permissions

def has_permission(self, request, view):
    if request.user.is_superuser:
        return True
    elif view.action == 'retrieve':
        return True
    else:
        return False

def has_object_permission(self, request, view, obj):
    if request.user.is_superuser:
        return True
    elif view.action == 'retrieve':
        return obj == request.user or request.user.is_staff
Run Code Online (Sandbox Code Playgroud)

  • 对于定义复杂的权限,这是一场噩梦。有没有更好的办法 ? (2认同)