Django REST framework - 权限

权限

认证或识别本身通常不足以获得信息或代码的访问权限。为此,请求访问的实体必须具有授权。

— Apple 开发者文档

权限、认证和限流共同决定了请求是否应该被允许访问。权限检查总是在视图的主要逻辑开始之前运行。权限检查通常会使用request.user.authrequest属性中的认证信息来确定传入的请求是否应该被允许。

权限用于授予或拒绝不同类别的用户访问 API 的不同部分。

REST framework 中最简单的权限样式是允许任何认证用户访问,拒绝任何未认证用户的访问。这对应于 REST framework 中的IsAuthenticated类。

稍微宽松一点的权限样式是允许认证用户完全访问,但允许未认证用户只读访问。这对应于 REST framework 中的IsAuthenticatedOrReadOnly类。

权限是如何确定的

REST framework 中的权限始终被定义为权限类的列表。

在运行视图的主要逻辑之前,会检查列表中的每个权限。如果任何一个权限检查失败,将引发exceptions.PermissionDeniedexceptions.NotAuthenticated异常,视图的主要逻辑将不会运行。

当权限检查失败时,将根据以下规则返回“403 禁止访问”或“401 未授权”响应:

  • 请求成功认证,但被拒绝访问。 — 将返回 HTTP 403 禁止访问响应。
  • 请求未成功认证,且优先级最高的认证类不使用WWW-Authenticate标头。 — 将返回 HTTP 403 禁止访问响应。
  • 请求未成功认证,且优先级最高的认证类使用WWW-Authenticate标头。 — 将返回带有适当WWW-Authenticate标头的 HTTP 401 未授权响应。

对象级权限

REST framework 权限还支持对象级权限。对象级权限用于确定是否允许用户对特定对象执行操作,这通常是模型实例。

当调用.get_object()时,REST framework 的通用视图会运行对象级权限。与视图级权限一样,如果用户不允许对给定对象执行操作,将引发exceptions.PermissionDenied异常。

如果你想在编写自定义视图时强制执行对象级权限,或者如果你想覆盖通用视图上的get_object方法,则需要在视图中显式调用.check_object_permissions(request, obj)方法,当你检索到对象时。

这将引发PermissionDeniedNotAuthenticated异常,或者如果视图具有适当的权限,则简单返回。

例如:

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

注意:除DjangoObjectPermissions外,rest_framework.permissions中提供的权限类没有实现用于检查对象权限的方法。

如果你想使用提供的权限类来检查对象权限,你必须通过子类化它们并在下面描述的“自定义权限”部分中实现has_object_permission()方法。

对象级权限的限制

出于性能原因,当返回对象列表时,通用视图不会自动将对象级权限应用于查询集中的每个实例。

通常,当你使用对象级权限时,你还需要适当地过滤查询集,以确保用户只能查看他们被允许查看的实例。

因为get_object()方法没有被调用,所以当创建对象时,has_object_permission()方法中的对象级权限不会被应用。为了限制对象创建,你需要在序列化器类中实现权限检查,或者覆盖视图集类的perform_create()方法。

设置权限策略

可以通过DEFAULT_PERMISSION_CLASSES设置全局默认权限策略。例如:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

如果没有指定,此设置默认允许无限制的访问:

'DEFAULT_PERMISSION_CLASSES': [
   'rest_framework.permissions.AllowAny',
]

还可以在每个视图或每个视图集的基础上,使用APIView类基视图设置认证策略。

from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

class ExampleView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

或者,如果你使用的是函数基视图的@api_view装饰器。

from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes([Is])
Authenticateddef example_view(request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)

注意:当你通过类属性或装饰器设置新的权限类时,你是在告诉视图忽略在settings.py文件中设置的默认列表。

只要它们继承自rest_framework.permissions.BasePermission,权限就可以使用标准的 Python 位运算符组合。例如,IsAuthenticatedOrReadOnly可以写成:

from rest_framework.permissions import BasePermission, IsAuthenticated, SAFE_METHODS
from rest_framework.response import Response
from rest_framework.views import APIView

class ReadOnly(BasePermission):
    def has_permission(self, request, view):
        return request.method in SAFE_METHODS

class ExampleView(APIView):
    permission_classes = [IsAuthenticated|ReadOnly]

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

注意:它支持 & (与)、| (或) 和 ~ (非)。

API 参考

AllowAny

AllowAny权限类将允许无限制的访问,无论请求是否经过认证。

这个权限不是严格需要的,因为你可以通过空使用列表或元组来实现相同的结果,但你可能会发现指定这个类很有用,因为它明确表达了意图。

IsAuthenticated

IsAuthenticated权限类将拒绝任何未认证用户的访问,否则允许访问。

这个权限适合你希望 API 仅对注册用户可用的情况。

IsAdminUser

IsAdminUser权限类将拒绝任何用户访问,除非user.is_staffTrue,在这种情况下将允许访问。

这个权限适合你希望 API 仅对一组受信任的管理员可用的情况。

IsAuthenticatedOrReadOnly

IsAuthenticatedOrReadOnly将允许认证用户执行任何请求。对于未认证用户的请求,只有请求方法是“”安全方法之一(GETHEADOPTIONS)时才允许。

这个权限适合你希望 API 允许匿名用户读取权限,并仅允许认证用户写入权限的情况。

DjangoModelPermissions

此权限类与 Django 的标准django.contrib.auth模型权限集成。此权限必须仅应用于具有.queryset属性或get_queryset()方法的视图。只有当用户已认证且具有相关的模型权限时,才会授予授权。适当的模型是通过检查get_queryset().modelqueryset.model确定的。

  • POST请求要求用户在模型上具有add权限。
  • PUTPATCH请求要求用户在模型上具有change权限。
  • DELETE请求要求用户在模型上具有delete权限。

默认行为也可以被覆盖以支持自定义模型权限。例如,你可能希望为GET请求包含view模型权限。

要使用自定义模型权限,可以通过子类化DjangoModelPermissions并设置.perms_map属性来实现。有关详细信息,请参阅源代码。

DjangoModelPermissionsOrAnonReadOnly

DjangoModelPermissions类似,但也允许未认证用户对 API 进行只读访问。

DjangoObjectPermissions

此权限类与 Django 的标准对象权限框架集成,允许对模型进行每个对象的权限控制。要使用此权限类,你还需要添加一个支持对象级权限的权限后端,如 django-guardian。

DjangoModelPermissions一样,此权限必须仅应用于具有.queryset属性或.get_queryset()方法的视图。只有当用户已认证且具有相关的每个对象的权限和相关的模型权限时,才会授予授权。

  • POST请求要求用户在模型实例上具有add权限。
  • PUTPATCH请求要求用户在模型实例上具有change权限。
  • DELETE请求要求用户在模型实例上具有delete权限。

注意DjangoObjectPermissions不需要django-guardian包,并且应该同样支持其他对象级后端。

DjangoModelPermissions一样,你可以通过子类化DjangoObjectPermissions并设置.perms_map属性来使用自定义模型权限。有关详细信息,请参阅源代码。

自定义权限

要实现自定义权限,可以通过子类化BasePermission并实现以下方法之一或两个:

  • .has_permission(self, request, view)
  • .has_object_permission(self, request, view, obj)

这些方法应返回True如果请求应该被授予访问权限,否则返回False

如果你需要测试请求是读取操作还是写入操作,你应该检查请求方法是否在SAFE_METHODS常量中,这是一个包含'GET''OPTIONS''HEAD'的元组。例如:

if request.method in permissions.SAFE_METHODS:
    # 检查只读请求的权限
else:
    # 检查写入请求的权限

注意:实例级别的has_object_permission方法仅在视图级别的has_permission检查已经通过后才会被调用。另外注意,为了使实例级别的检查运行,视图代码应该显式调用.check_object_permissions(request, obj)。如果你使用的是通用视图,这将默认为你处理。 (函数基视图需要显式检查对象权限,失败时引发PermissionDenied异常。)

自定义权限如果测试失败将引发PermissionDenied异常。要更改异常关联的错误消息,请在自定义权限上直接实现message属性。否则将使用PermissionDenieddefault_detail属性。同样,要更改异常关联的代码标识符,请在自定义权限上直接实现code属性,否则将使用PermissionDenieddefault_code属性。

from rest_framework import permissions

class CustomerAccessPermission(permissions.BasePermission):
    message = 'Adding customers not allowed.'

    def has_permission(self, request, view):
         ...

示例

以下是一个检查传入请求的 IP 地址是否在封禁列表中的权限类示例,并在 IP 被封禁时拒绝请求。

from rest_framework import permissions

class BlocklistPermission(permissions.BasePermission):
    """
    全局权限检查以阻止 IP。
    """

    def has_permission(self, request, view):
        ip_addr = request.META['REMOTE_ADDR']
        blocked = Blocklist.objects.filter(ip_addr=ip_addr).exists()
        return not blocked

除了全局权限(适用于所有传入请求)外,你还可以创建对象级权限,这些权限仅适用于影响特定对象实例的操作。例如:

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    对象级权限,仅允许对象所有者编辑它。
    假设模型实例具有`owner`属性。
    """

    def has_object_permission(self, request, view, obj):
        # 允许任何请求的读取权限,
        # 因此我们始终允许 GET、HEAD 或 OPTIONS 请求。
        if request.method in permissions.SAFE_METHODS:
            return True

        # 实例必须具有名为`owner`的属性。
        return obj.owner == request.user

请注意,通用视图将检查适当的对象级权限,但如果你编写自定义视图,你需要确保自己检查对象级权限。你可以在视图中通过调用self.check_object_permissions(request, obj)来实现这一点,一旦你有了对象实例。如果对象级权限检查失败,此调用将引发适当的APIException,否则将简单返回。

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

访问限制方法概览

REST framework 提供三种不同的方法来逐个自定义访问限制。这些适用于不同场景,并具有不同的效果和限制。

  • queryset/get_queryset():限制数据库中现有对象的一般可见性。查询集限制将列出哪些对象以及可以修改或删除哪些对象。get_queryset()方法可以根据当前操作应用不同的查询集。
  • permission_classes/get_permissions():基于当前操作、请求和目标对象的一般权限检查。对象级权限只能应用于检索、修改和删除操作。列表和创建操作的权限检查将应用于整个对象类型。(在列表的情况下:受查询集中的限制。)
  • serializer_class/get_serializer():影响所有输入和输出对象的实例级限制。序列化器可能具有对请求上下文的访问权限。get_serializer()方法可以根据当前操作应用不同的序列化器。

下表列出了访问限制方法及其对操作的控制级别。

queryset permission_classes serializer_class
操作:列表 全局 全局 对象级*
操作:创建 全局 对象级
操作:检索 全局 对象级 对象级
操作:更新 全局 对象级 对象级
操作:部分更新 全局 对象级 对象级
操作:销毁 全局 对象级
可以引用操作进行决策 否** 否**
可以引用请求进行决策 否**

*序列化器类不应在列表操作中引发PermissionDenied异常,否则将无法返回整个列表。

** get_*()方法可以访问当前视图,并可以根据请求或操作返回不同的序列化器或查询集实例。

第三方软件包

以下第三方软件包也可用。

DRF - 访问策略

Django REST - 访问策略软件包提供了一种方法,可以通过附加到视图集或函数基视图的声明性策略类来定义复杂的访问规则。策略以 JSON 格式定义,类似于 AWS 的身份和访问管理策略。

组合权限

组合权限软件包提供了一种简单的方法,使用小而可重用的组件来定义复杂和多深度的权限对象。

REST 条件

REST 条件软件包是另一个扩展,用于以简单和方便的方式构建复杂的权限。该扩展允许使用逻辑运算符组合权限。

DRY Rest 权限

DRY Rest 权限软件包提供了为默认和自定义操作定义不同权限的能力。此软件包适用于权限源自应用程序数据模型中定义的关系的应用程序。它还支持通过 API 的序列化器将权限检查返回给客户端应用程序。此外,它支持为默认和自定义列表操作添加权限,以限制它们按用户检索的数据。

Django REST framework 角色

Django REST framework 角色软件包使根据多种类型的用户参数化 API 更加容易。

REST framework 角色

REST framework 角色使基于角色保护视图变得非常容易。最重要的是,它允许以干净且易读的方式将可访问性逻辑与模型和视图解耦。

Django REST framework API 密钥

Django REST framework API 密钥软件包提供了权限类、模型和帮助程序,以将 API 密钥授权添加到你的 API。它可以用来授权没有用户账户的内部或第三方后台服务(即机器)。API 密钥使用 Django 的密码哈希基础设施安全存储,并且可以随时在 Django 管理界面中查看、编辑和撤销。

Django REST framework 角色过滤器

Django REST framework 角色过滤器软件包提供了对多种角色进行简单过滤的功能。

Django REST framework PSQ

Django REST framework PSQ 软件包是一个扩展,支持基于权限规则的 action 级权限类序列化器类查询集

你可能感兴趣的:(djangopython)