权限
认证或识别本身通常不足以获得信息或代码的访问权限。为此,请求访问的实体必须具有授权。
— Apple 开发者文档
权限、认证和限流共同决定了请求是否应该被允许访问。权限检查总是在视图的主要逻辑开始之前运行。权限检查通常会使用request.user
和.authrequest
属性中的认证信息来确定传入的请求是否应该被允许。
权限用于授予或拒绝不同类别的用户访问 API 的不同部分。
REST framework 中最简单的权限样式是允许任何认证用户访问,拒绝任何未认证用户的访问。这对应于 REST framework 中的IsAuthenticated
类。
稍微宽松一点的权限样式是允许认证用户完全访问,但允许未认证用户只读访问。这对应于 REST framework 中的IsAuthenticatedOrReadOnly
类。
权限是如何确定的
REST framework 中的权限始终被定义为权限类的列表。
在运行视图的主要逻辑之前,会检查列表中的每个权限。如果任何一个权限检查失败,将引发exceptions.PermissionDenied
或exceptions.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)
方法,当你检索到对象时。
这将引发PermissionDenied
或NotAuthenticated
异常,或者如果视图具有适当的权限,则简单返回。
例如:
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_staff
为True
,在这种情况下将允许访问。
这个权限适合你希望 API 仅对一组受信任的管理员可用的情况。
IsAuthenticatedOrReadOnly
IsAuthenticatedOrReadOnly
将允许认证用户执行任何请求。对于未认证用户的请求,只有请求方法是“”安全方法之一(GET
、HEAD
或OPTIONS
)时才允许。
这个权限适合你希望 API 允许匿名用户读取权限,并仅允许认证用户写入权限的情况。
DjangoModelPermissions
此权限类与 Django 的标准django.contrib.auth
模型权限集成。此权限必须仅应用于具有.queryset
属性或get_queryset()
方法的视图。只有当用户已认证且具有相关的模型权限时,才会授予授权。适当的模型是通过检查get_queryset().model
或queryset.model
确定的。
POST
请求要求用户在模型上具有add
权限。PUT
和PATCH
请求要求用户在模型上具有change
权限。DELETE
请求要求用户在模型上具有delete
权限。
默认行为也可以被覆盖以支持自定义模型权限。例如,你可能希望为GET
请求包含view
模型权限。
要使用自定义模型权限,可以通过子类化DjangoModelPermissions
并设置.perms_map
属性来实现。有关详细信息,请参阅源代码。
DjangoModelPermissionsOrAnonReadOnly
与DjangoModelPermissions
类似,但也允许未认证用户对 API 进行只读访问。
DjangoObjectPermissions
此权限类与 Django 的标准对象权限框架集成,允许对模型进行每个对象的权限控制。要使用此权限类,你还需要添加一个支持对象级权限的权限后端,如 django-guardian。
与DjangoModelPermissions
一样,此权限必须仅应用于具有.queryset
属性或.get_queryset()
方法的视图。只有当用户已认证且具有相关的每个对象的权限和相关的模型权限时,才会授予授权。
POST
请求要求用户在模型实例上具有add
权限。PUT
和PATCH
请求要求用户在模型实例上具有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
属性。否则将使用PermissionDenied
的default_detail
属性。同样,要更改异常关联的代码标识符,请在自定义权限上直接实现code
属性,否则将使用PermissionDenied
的default_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 级权限类、序列化器类和查询集。