我正在使用Django REST Framework编写API,我想知道在使用基于类的视图时是否可以为每个方法指定权限。
Reading the documentation我看到,如果您正在编写基于函数的视图,只需使用@permission_classes
装饰器覆盖您想要使用权限保护的视图的功能,这很容易。但是,在使用带有APIView
类的CBV时,我没有看到相同的方法,因为那时我使用permission_classes
属性指定了完整类的权限,但是那时将应用所有课程方法(get
,post
,put
...)。
那么,是否可以使用CBV编写API视图,并为视图类的每个方法指定不同的权限?
答案 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)
答案 5 :(得分:1)
我遇到了类似的问题。
我想允许未经身份验证的POST,但不允许未经身份验证的GET。
未经身份验证的公众成员可以提交项目,但只有经过身份验证的管理员用户才能检索已提交项目的列表。
因此,我为POST建立了一个自定义权限类-UnauthenticatedPost
-然后将权限类别的列表设置为IsAuthentictaed
或UnauthenticatedPost
。
请注意,我仅通过使用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