Django REST框架 - 每个方法单独的权限

时间:2013-11-04 17:48:48

标签: python django rest permissions django-rest-framework

我正在使用Django REST Framework编写API,我想知道在使用基于类的视图时是否可以为每个方法指定权限。

Reading the documentation我看到,如果您正在编写基于函数的视图,只需使用@permission_classes装饰器覆盖您想要使用权限保护的视图的功能,这很容易。但是,在使用带有APIView类的CBV时,我没有看到相同的方法,因为那时我使用permission_classes属性指定了完整类的权限,但是那时将应用所有课程方法(getpostput ...)。

那么,是否可以使用CBV编写API视图,并为视图类的每个方法指定不同的权限?

8 个答案:

答案 0 :(得分:46)

我在使用CBV时遇到了同样的问题,因为根据请求方法我有相当复杂的权限逻辑。

我想出的解决方案是使用本页底部列出的第三方'rest_condition'应用

http://www.django-rest-framework.org/api-guide/permissions

https://github.com/caxap/rest_condition

我只是拆分权限流逻辑,以便每个分支都会运行,具体取决于请求方法。

from rest_condition import And, Or, Not

class MyClassBasedView(APIView):

    permission_classes = [Or(And(IsReadOnlyRequest, IsAllowedRetrieveThis, IsAllowedRetrieveThat),
                             And(IsPostRequest, IsAllowedToCreateThis, ...),
                             And(IsPutPatchRequest, ...),
                             And(IsDeleteRequest, ...)]

因此,“或”确定应根据请求方法运行权限的哪个分支,并且“And”包装与接受的请求方法相关的权限,因此所有权限都必须通过以获得授予权限。您还可以在每个流程中混合使用“或”,“和”和“不”来创建更复杂的权限。

运行每个分支的权限类看起来像这样,

class IsReadyOnlyRequest(permissions.BasePermission):

    def has_permission(self, request, view):
        return request.method in permissions.SAFE_METHODS


class IsPostRequest(permissions.BasePermission):

    def has_permission(self, request, view):
        return request.method == "POST"


... #You get the idea

答案 1 :(得分:39)

权限适用于整个View类,但您可以在授权决策中考虑请求的各个方面(如GET或POST等方法)。

请参阅内置IsAuthenticatedOrReadOnly作为示例:

SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']

class IsAuthenticatedOrReadOnly(BasePermission):
    """
    The request is authenticated as a user, or is a read-only request.
    """

    def has_permission(self, request, view):
        if (request.method in SAFE_METHODS or
            request.user and
            request.user.is_authenticated()):
            return True
        return False

答案 2 :(得分:3)

我知道这是一个老问题,但是最近我遇到了同样的问题,并想分享我的解决方案(因为公认的答案与我所需的不完全相同)。 @GDorn的答案使我步入正轨,但由于ViewSet

,它仅适用于self.action s

我已经解决了创建自己的装饰器的问题:

def method_permission_classes(classes):
    def decorator(func):
        def decorated_func(self, *args, **kwargs):
            self.permission_classes = classes
            return func(self, *args, **kwargs)
        return decorated_func
    return decorator

我的装饰器没有像内置装饰器那样在函数上设置permission_classes属性,而是包装了调用并在被调用的视图实例上设置了权限类。这样,普通的get_permissions()不需要任何更改,因为它仅依赖于self.permission_classes

用例示例:

from rest_framework import views, permissions

class MyView(views.APIView):
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)  # used for default APIView endpoints
    queryset = MyModel.objects.all()
    serializer_class = MySerializer


    @method_permission_classes((permissions.IsAdminUser,))  # overrides IsAuthenticatedOrReadOnly
    def delete(self, request, id):
        instance = self.get_object()  # ...

希望这可以帮助遇到相同问题的人!

答案 3 :(得分:3)

这个问题是关于 APIView 实例,但对于登陆这里寻找使用 @action 中的 ViewSets 装饰器的每个方法权限覆盖的任何人:


class SandwichViewSet(ModelViewSet):
  permission_classes = [IsAuthenticated]

  @action(..., permission_classes=[CanSeeIngredients])
  def retrieve__ingredients(self, request):
    ...

答案 4 :(得分:2)

如果您使用22AA11BBViewSet,我认为覆盖ModelViewSet可以解决问题。
看看djoser是如何处理的。

示例:

get_permissions

答案 5 :(得分:1)

我遇到了类似的问题。

我想允许未经身份验证的POST,但不允许未经身份验证的GET。

未经身份验证的公众成员可以提交项目,但只有经过身份验证的管理员用户才能检索已提交项目的列表。

因此,我为POST建立了一个自定义权限类-UnauthenticatedPost-然后将权限类别的列表设置为IsAuthentictaedUnauthenticatedPost

请注意,我仅通过使用http_method_names = ['get', 'post']设置允许的方法来允许获取和发布。

from django.http import HttpResponse
from rest_framework import viewsets
from rest_framework.permissions import BasePermission, IsAuthenticated
from MyAPI.serializers import MyAPISerializer
from MyAPI.models import MyAPI


class UnauthenticatedPost(BasePermission):
    def has_permission(self, request, view):
        return request.method in ['POST']


class MyAPIViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated|UnauthenticatedPost]
    queryset = MyAPI.objects.all().order_by('-TimeSubmitted')
    serializer_class = MyAPISerializer
    http_method_names = ['get', 'post']

答案 6 :(得分:0)

我遇到了这个问题,真的想使用@permission_classes装饰器来标记具有特定权限的某些自定义视图方法。我最终想到了mixin:

class PermissionsPerMethodMixin(object):
    def get_permissions(self):
        """
        Allows overriding default permissions with @permission_classes
        """
        view = getattr(self, self.action)
        if hasattr(view, 'permission_classes'):
            return [permission_class() for permission_class in view.permission_classes]
        return super().get_permissions()

一个示例用例:

from rest_framework.decorators import action, permission_classes  # other imports elided

class MyViewset(PermissionsPerMethodMixin, viewsets.ModelViewSet):
    permission_classes = (IsAuthenticatedOrReadOnly,)  # used for default ViewSet endpoints
    queryset = MyModel.objects.all()
    serializer_class = MySerializer

    @action(detail=False, methods=['get'])
    @permission_classes((IsAuthenticated,))  # overrides IsAuthenticatedOrReadOnly
    def search(self, request):
        return do_search(request)  # ...

答案 7 :(得分:0)

在获取GET,PUT和POST的不同权限时,我们面临着同样的挑战,并且已经使用自定义的权限类解决了这一问题:

from rest_framework import permissions

class HasRequiredPermissionForMethod(permissions.BasePermission):
    get_permission_required = None
    put_permission_required = None
    post_permission_required = None

    def has_permission(self, request, view):
        permission_required_name = f'{request.method.lower()}_permission_required'
        if not request.user.is_authenticated:
            return False
        if not hasattr(view, permission_required_name):
            view_name = view.__class__.__name__
            self.message = f'IMPLEMENTATION ERROR: Please add the {permission_required_name} variable in the API view class: {view_name}.'
            return False

        permission_required = getattr(view, permission_required_name)
        if not request.user.has_perm(permission_required):
            self.message = f'Access denied. You need the {permission_required} permission to access this service with {request.method}.'
            return False

        return True

我们像这样在我们的API中使用它:

class MyAPIView(APIView):
    permission_classes = [HasRequiredPermissionForMethod]
    get_permission_required = 'permission_to_read_this'
    put_permission_required = 'permission_to_update_this'
    post_permission_required = 'permission_to_create_this'

    def get(self, request):
        # impl get

    def put(self, request):
        # impl put

    def post(self, request):
        # impl post