DRF-Authentication&Permissions
Authentication
身份验证是将传入Request
与一组标识凭据 (如请求来自的用户或与之签名的令牌) 相关联的机制。然后, 权限和限制策略可以使用这些凭据来确定是否应允许该请求。
DRF提供了许多现成的身份验证方案, 还允许您实现自定义方案。
身份验证始终在视图一开始、在进行权限和限制检查之前以及在允许任何其他代码继续之前运行。
request.user
属性通常会被设置成contrib.auth
包内User
类的实例。
request.auth
属性用于任何身份验证信息, 例如, 它可用于表示与请求签名的身份验证令牌。
如何进行身份验证
首先,身份验证方案被定义为一个类的列表。列表里的每个类都是一种身份验证方式。DRF会尝试按照列表里的每个类进行身份验证。最终将第一个类的返回结果设置在request.user
和request.auth
值班中。
如果,列表为空,也就是没有身份验证,request.user
会被设置成django.contrib.auth.models.AnoymouseUser
的一个实例,并且request.auth
会被设置成None
。
未经验证请求的request.user
和request.auth
的值可以通过UNAUTHENTICATED_USER
和UNAUTHENTICATED_TOKEN
设置修改。
设置身份验证
全局设置
使用settings.py
文件中的DEFAULT_AUTHENTICATION_CLASSES
选项进行身份验证的全局配置:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
视图内配置
- 使用
authentication_classes
选项在APIView
内配置:
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
def get(self, request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
- 使用
@authentication_classes
装饰器在@api_view
装饰器后设置:@api_view(['GET']) @authentication_classes((SessionAuthentication, BasicAuthentication)) @permission_classes((IsAuthenticated,)) def example_view(request, format=None): content = { 'user': unicode(request.user), # `django.contrib.auth.User` instance. 'auth': unicode(request.auth), # None } return Response(content)
response
当未经验证的请求没有权限访问时,可能会返回两种不同的错误码:
- HTTP 401 Unauthorized
- HTTP 403 Permission Denied
HTTP 401
response通常会包含一个WWW-Authenticate
头部, 来介绍客户端应如何验证。HTTP 403
response不包含WWW-Authenticate
头部。
通常在视图中第一个验证的类会被用来决定response
的类型。
当一个请求已经成功完成身份验证,但仍然没有权限执行请求。这个时候,就会返回403 Permission Denied
response。
Apache mod_wsgi specific configuration
请注意, 如果使用 mod _ wsgi
部署到 Apache
, 则默认情况下不会将授权header
传递给WSGI
应用程序, 因为假定身份验证将由Apache
处理, 而不是在应用程序级别处理。
如果要部署到Apache
, 并使用任何非会话身份验证, 则需要显式配置 mod _ wsgi
, 以便将所需的header
传递给应用程序。这可以通过在适当的上下文中指定WSGIPassAuthorization
指令并将其设置为 'On'
来实现。
# this can go in either server config, virtual host, directory or .htaccess
WSGIPassAuthorization On
认证类型
默认内置认证类型
第三方认证库
https://www.django-rest-framework.org/api-guide/authentication/#third-party-packages
比如:
自定义认证
若要实现自定义身份验证方案, 需要继承 BaseAuthentication
类并重写.authenticate(self, request)
方法。如果身份验证成功, 该方法应返回(user, auth)
, 否则返回None
。
在某些情况下, 您可能希望从.authenticate()
抛出AuthenticationFailed
异常, 而不是返回 None
.
通常, 您应该采取的方法是:
- 如果未尝试身份验证, 则返回
None
。仍将检查正在使用的任何其他身份验证方案 - 如果尝试了身份验证, 但失败, 则引发
AuthenticationFailed
异常。无论是否进行任何权限检查, 都将立即返回错误响应, 而无需检查任何其他身份验证方案。
您也可以重写.authenticate_header(self, request)
方法。如果实现, 它应该返回一个字符串, 该字符串将在HTTP 401 Unauthorized
response中用作WWW-Authenticate
头部的值。
Example
from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions
class ExampleAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
username = request.META.get('X_USERNAME')
if not username:
return None
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise exceptions.AuthenticationFailed('No such user')
return (user, None)
Permissions
权限用于授予或拒绝不同类别的用户访问API
不同部分。
最简单的权限是允许对任何经过身份验证的用户进行访问, 并拒绝对任何未经身份验证的用户进行访问。这对应于DRF 的IsAuthenticated
类。
怎样确定权限
DRF中的Permissions
始终被定义为一个权限类的列表。
在运行视图的主体之前, 将检查列表中的每个权限。如果任何权限检查失败, 则会引发exceptions.PermissionDenied
或exceptions.NotAuthenticated
异常,并且视图的主体将不会运行。
当权限检查失败时, 将根据以下规则返回 403 Forbidden
或 401 Unauthorized
响应:
请求已成功通过身份验证, 但权限被拒绝。-将返回
HTTP 403 Forbidden
响应。请求未成功通过身份验证, 并且最高优先级身份验证类不使用
WWW-Authenticate
header。-将返回HTTP 403 Forbidden
响应。请求未成功通过身份验证, 并且最高优先级身份验证类确实使用
WWW-Authenticate
header。-将返回具有适当的WWW-Authenticate
header的HTTP 401 Unauthorized
响应。
Object level permissions
DRF还支持对象级权限控制。对象级别权限用于确定是否应允许用户对特定对象执行操作, 该对象通常是模型实例。
当调用.get_object()
时, 对象级别权限由DRF的泛型视图运行。与视图级别权限一样, 如果不允许用户对给定对象执行操作, 则将引发exceptions.PermissionDenied
异常。
如果您正在编写自己的视图并希望强制实施对象级别权限, 或者如果您在泛型视图上重写get_object
法, 则当您检索对象时,需要在视图上显式调用.check_object_permissions(request, obj)
方法。
这将引发PermissionDenied
或NotAuthenticated
异常, 或者简单返回视图是否拥有合适的权限。
For example:
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
Setting the permission policy
全局设置
使用settings.py
文件中的DEFAULT_PERMISSION_CLASSES
选项进行设置:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
当没有指定时,此设置默认为允许不受限制的访问:
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
)
视图内设置
- 使用
permission_classes
在APIView
内设置: ```Python 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)
2. 使用`@permission_classes`在`@api_view`后使用:
```Python
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)
当使用类属性或者装饰设置权限类列表后,则自动忽略了全局配置中的权限列表类。
如果权限从rest_framework.permissions.BasePermission
继承, 则可以使用标准 python 按位运算符组成权限。例如, IsAuthenticatedOrReadOnly
可以写成:
from rest_framework.permissions import BasePermission, IsAuthenticated
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)
仅支持
&
与 和|
或。
权限类
内置权限类
第三方权限类库
自定义权限
若要实现自定义权限, 请重写BasePermission
并实现以下方法中的一种或两种:
.has_permission(self, request, view)
.has_object_permission(self, request, view, obj)
如果授予请求访问权限, 则方法应返回True
, 否则返回False
。
如果需要测试请求是读取操作还是写入操作, 则应对照常量SAFE_METHODS
检查请求方法, 后者是包含'GET'
,'OPTIONS'
和'HEAD'
的元组。For example:
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 _ 权限 (请求、obj)。
。如果您使用的是通用视图, 则默认情况下将为您处理此问题。(基于函数的视图需要显式检查对象权限, 并在失败时抛出PermissionDenied
。
如果测试失败, 自定义权限将引发PermissionDenied
异常。若要更改与异常关联的错误消息, 请直接在自定义权限上实现message
属性。否则, 将使用PermissionDenied
中的default_detail
属性。
from rest_framework import permissions
class CustomerAccessPermission(permissions.BasePermission):
message = 'Adding customers not allowed.'
def has_permission(self, request, view):
...
Examples
下面是一个权限类的示例, 该示例根据黑名单检查传入请求的 ip 地址, 并在 ip 已被列入黑名单时拒绝该请求。
from rest_framework import permissions
class BlacklistPermission(permissions.BasePermission):
"""
Global permission check for blacklisted IPs.
"""
def has_permission(self, request, view):
ip_addr = request.META['REMOTE_ADDR']
blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
return not blacklisted
除了针对所有传入请求运行的全局权限外, 还可以创建对象级权限, 这些权限仅针对影响特定对象实例的操作运行。例如:
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
, 否则将简单地返回