权限
身份验证或识别本身通常不足以获取信息或代码的访问权限。为此,请求访问的实体必须具有授权。
与 身份验证 和 限制 一起,权限决定是否授予或拒绝请求访问权限。
权限检查总是在视图的开始阶段运行,在允许任何其他代码继续执行之前。权限检查通常会使用 request.user
和 request.auth
属性中的身份验证信息来确定是否应允许传入请求。
权限用于向不同类别的用户授予或拒绝访问 API 不同部分的权限。
最简单的权限样式将允许任何经过身份验证的用户访问,并拒绝任何未经身份验证的用户访问。这对应于 REST 框架中的 IsAuthenticated
类。
稍微不那么严格的权限样式将允许经过身份验证的用户完全访问,但允许未经身份验证的用户只读访问。这对应于 REST 框架中的 IsAuthenticatedOrReadOnly
类。
如何确定权限
REST 框架中的权限始终定义为权限类的列表。
在运行视图的主体之前,将检查列表中的每个权限。如果任何权限检查失败,将引发 exceptions.PermissionDenied
或 exceptions.NotAuthenticated
异常,并且视图的主体将不会运行。
当权限检查失败时,将根据以下规则返回“403 禁止”或“401 未授权”响应
- 请求已成功通过身份验证,但权限被拒绝。— 将返回 HTTP 403 禁止响应。
- 请求未成功通过身份验证,并且优先级最高的身份验证类不使用
WWW-Authenticate
标头。— 将返回 HTTP 403 禁止响应。 - 请求未通过身份验证,并且最高优先级的身份验证类确实使用
WWW-Authenticate
头。— 将返回带有适当WWW-Authenticate
头的 HTTP 401 未授权响应。
对象级别权限
REST 框架权限还支持对象级权限控制。对象级权限用于确定是否允许用户对特定对象(通常是模型实例)执行操作。
当调用.get_object()
时,REST 框架的通用视图会运行对象级权限。与视图级权限一样,如果用户无权对给定对象执行操作,则会引发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()
方法中的对象级别权限。为了限制对象创建,您需要在 Serializer 类中实现权限检查,或覆盖 ViewSet 类的 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([IsAuthenticated])
def 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
属性来使用自定义模型权限。有关详细信息,请参阅源代码。
注意:如果您需要对 GET
、HEAD
和 OPTIONS
请求的对象级别 view
权限,并且对对象级别权限后端使用 django-guardian,您需要考虑使用 djangorestframework-guardian2
包 提供的 DjangoObjectPermissionsFilter
类。它确保列表端点仅返回包含用户具有适当查看权限的对象的结果。
自定义权限
要实现自定义权限,请覆盖 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:
# Check permissions for read-only request
else:
# Check permissions for write request
注意:只有在视图级别 has_permission
检查已通过的情况下,才会调用实例级别的 has_object_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):
"""
Global permission check for blocked IPs.
"""
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):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Instance must have an attribute named `owner`.
return obj.owner == request.user
请注意,通用视图将检查适当的对象级别权限,但如果您编写自己的自定义视图,您需要确保自己检查对象级别权限检查。您可以通过在获取对象实例后从视图调用 self.check_object_permissions(request, obj)
来执行此操作。如果任何对象级别权限检查失败,此调用将引发适当的 APIException
,否则将直接返回。
还要注意,通用视图仅针对检索单个模型实例的视图检查对象级别权限。如果您需要列表视图的对象级别筛选,您需要单独筛选查询集。有关更多详细信息,请参阅 筛选文档。
访问限制方法概述
REST 框架提供了三种不同的方法来逐案自定义访问限制。它们适用于不同的场景,具有不同的效果和限制。
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 - Access Policy 包提供了一种在声明式策略类中定义复杂访问规则的方法,这些策略类附加到视图集或基于函数的视图。这些策略在 JSON 中定义,其格式类似于 AWS 的身份和访问管理策略。
组合权限
Composed Permissions 包提供了一种简单的方法来定义复杂且多深度(带有逻辑运算符)的权限对象,使用小型且可重用的组件。
REST 条件
REST Condition 包是另一个用于以简单且便捷的方式构建复杂权限的扩展。该扩展允许你将权限与逻辑运算符结合起来。
DRY Rest 权限
DRY Rest Permissions 包提供了为各个默认操作和自定义操作定义不同权限的能力。此包适用于权限源自应用程序数据模型中定义的关系的应用程序。它还支持通过 API 的序列化器将权限检查返回给客户端应用程序。此外,它还支持向默认列表操作和自定义列表操作添加权限,以限制他们根据每个用户检索的数据。
Django Rest Framework 角色
Django Rest Framework Roles 包让根据多种类型的用户对你的 API 进行参数化变得更加容易。
Rest Framework 角色
Rest Framework Roles 让根据角色保护视图变得非常容易。最重要的是,它允许你以一种清晰易读的方式将可访问性逻辑与模型和视图解耦。
Django REST Framework API 密钥
Django REST Framework API Key 软件包提供权限类、模型和帮助程序,以向您的 API 添加 API 密钥授权。它可用于授权内部或第三方后端和服务(即机器),这些后端和服务没有用户帐户。API 密钥使用 Django 的密码哈希基础架构安全地存储,并且可以在 Django 管理中随时查看、编辑和吊销。
Django Rest Framework 角色过滤器
Django Rest Framework Role Filters 软件包提供对多种类型的角色进行简单的筛选。
Django Rest Framework PSQ
Django Rest Framework PSQ 软件包是一个扩展,它支持基于权限规则的基于操作的 permission_classes、serializer_class 和 queryset。