赞
踩
身份认证是将传入的请求与一组标识凭据(例如发送该请求的用户或其签名的token)相关联的机制。然后,“权限”和“限流”可以使用这些凭据来确定是否应允许该请求。
DRF提供了几种开箱即用的身份认证方案,还允许我们自定义认证方案。
DRF内置了几个认证类,如果需要可以参考官方文档:传送门
两个与认证相关的request属性:
request.user
属性通常被设置为contrib.auth
包中User
类的一个实例;
request.auth
属性用于任何其他身份认证信息,例如,它可以用于表示请求签名的身份认证令牌(token)。
身份认证总是在视图的最开始运行,在执行视图其他任何其他代码之前执行。
身份认证方案被定义为一个列表,DRF将尝试使用列表中的每个类进行身份认证,并使用第一个成功身份认证的类的返回值设置request.user
和request.auth
。
如果没有类进行认证,request.user
将被设置成django.contrib.auth.models.AnonymousUser
(django的匿名用户)的实例,request.auth
将被设置成None
。
对于未经身份认证的请求,可以使用UNAUTHENTICATED_USER
和UNAUTHENTICATED_TOKEN
设置修改request.user
和request.auth
的值。
要实现自定义的认证类,就要继承BaseAuthentication
类并且重写authenticate(self, request)
方法。如果认证成功,该方法应返回(user, auth)
的二元元组,否则返回None。
在某些情况下,也可以不返回None,而是抛出AuthenticationFailed
异常:
AuthenticationFailed
异常。这将立即返回一个错误响应,无论是否进行权限检查,也不继续检查其他任何身份认证方案。通常情况下,我们会在APP文件夹下创建一个py文件,单独存放这些认证类:
from app01 import models
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class authlogin(BaseAuthentication):
def authenticate(self, request):
token = request.GET.get('token') # 来访问数据的时候需要携带的token
res = models.UserToken.objects.filter(token = token).first() # 如果和数据库的值不一致,不允许访问数据
if res:
return (res.user,token)# 返回元组,第一个值会给request.user ,第二值会给request.auth
else:
raise AuthenticationFailed('您没有登录')
局部使用:
在视图中设置以下属性,则只会对该视图生效:
class MyView(APIView): # 只对APIView的子类有效
authentication_classes = [认证类, 认证类……]
……
注意:如果列表为空,则说明对视图不进行认证限制,相当于局部禁用认证。
全局使用:
在项目配置文件settings.py中,写入以下内容,则会对全部视图生效:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"app名称.自建py文件.自定义认证类",
……
]
}
注意:如果配置多个认证类,则要把返回元组的类放在最后。
确定好用户的身份后,就需要确定具体的用户权限了。权限用来确定用户是否可以对API进行访问,DRF自带了一些权限,并且支持我们自定义权限。
DRF内置权限的详情可以参考官方文档:传送门
权限检查始终在视图的身份认证代码之后,其他任何代码之前执行。
与系统认证方案类似,权限也是通过一个列表进行设置,列表中包含的也是一个个的类。
在运行视图主体之前,将检查列表中的每个权限。如果任何权限检查失败,将抛出exceptions.PermissionDenied
或exceptions.NotAuthenticated
异常,并且视图的主体将不会运行。
上面说的是视图级别权限,DRF也支持对象级权限。对象级权限用于确定是否应该允许用户对特定对象进行操作,该对象通常是一个模型实例。
当调用get_object()
方法时,对象级权限由DRF的通用视图(GenericAPIView及其子类)运行。如果不允许用户对给定对象进行操作,则会引发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
对象级别权限的限制:
出于性能原因,通用视图在返回对象列表时不会将对象级权限应用于查询集中的每个实例。
所以在通常情况下,当我们使用对象级权限时,还需要适当地过滤查询集,以确保用户只能看到他们被允许查看的实例。
要实现自定义权限,就要写一个类并继承BasePermission
类,然后重写以下方法中的两个或一个:
has_permission(self, request, view)
:用于视图级别权限。has_object_permission(self, request, view, obj
:用于对象级别权限。如果请求被授予访问权限,则该方法应返回True,否则返回False。
如果测试失败,自定义权限将抛出一个PermissionDenied
异常。若要更改与异常关联的错误消息,请在自定义权限上直接实现message
属性。否则,将使用PermissionDenied
的default_detail
属性。
from rest_framework import permissions
class CustomerPermission(permissions.BasePermission):
message = '没有权限!'
def has_permission(self, request, view):
...
如果我们需要测试请求是读取操作还是写入操作,则应该根据常量SAFE_METHODS
检查请求方法,SAFE_METHODS
是包含’GET’、'OPTIONS’和’HEAD’的元组。例如:
if request.method in permissions.SAFE_METHODS:
# 检查只读请求的权限
else:
# 检查读取请求的权限
注意: 仅当视图级
has_permission
检查已通过时,才会调用对象级has_object_permission
方法。另外,为了运行对象级别检查,视图代码应显式调用
check_object_permissions(request, obj)
。但如果使用的是通用视图(GenericAPIView及其子类),那么默认会替我们自动处理。
局部使用:
在视图中设置以下属性,则只会对该视图生效:
class MyView(APIView): # 只对APIView的子类有效
……
permission_classes = [权限类, 权限类……]
……
注意:如果列表为空,则说明对视图不进行认证限制,相当于局部禁用认证。
全局使用:
在项目配置文件settings.py中,写入以下内容,则会对全部视图生效:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
"app名称.自建py文件.自定义认证类",
……
]
}
限流类似于权限,它决定是否应该授权请求,二者配合使用,用于控制客户端可以向API发出的请求速率。 用法也与权限极为相似。
与权限和身份验证一样,DRF中的限流总是定义为一个列表,内部包含若干个类。
在运行视图主体之前,会检查列表中的每个限流。如果任何限流检查失败,则抛出exceptions.Throttled
异常,并且视图的主体将不会运行。
AnonRateThrottle
只会限制未经认证的用户(匿名用户或者未登录用户)。传入请求的IP地址用于生成一个惟一的密钥,以此来限流。
UserRateThrottle
将限制用户在API中给定的请求速率。用户id用于生成一个唯一的键,以此来限流。未经身份验证的请求则会像AnonRateThrottle
那样,使用IP地址用于生成一个惟一的密钥,以此来限流。
关于内置限流的更多信息,请参考官方文档:传送门
设置速率:
无论是局部使用还是全局使用,都要在项目配置文件settings.py中设置速率:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
DEFAULT_THROTTLE_RATES
可以使用second
、minute
、 hour
或 day
作为限流的周期。
局部使用:
在视图中设置以下属性,则只会对该视图生效:
class MyView(APIView): # 只对APIView的子类有效
……
throttle_classes = [UserRateThrottle,……]
……
注意:如果列表为空,则说明对视图不进行认证限制,相当于局部禁用认证。
全局使用:
在项目配置文件settings.py中写入以下内容:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
DRF的通用列表视图(generic list views)的默认行为是返回模型的整个查询集。通常,我们希望API限制queryset返回的项,此时过滤就派上用场了。
对于GenericAPIView
的子视图来说,最简单的过滤任意查询集的方法就是重写它的get_queryset()
方法。
如果我们想要过滤queryset,确保只返回与当前发出请求的已验证用户相关的结果。那么就可以通过基于request.user
的值进行过滤来实现。
from myapp.models import Purchase
from myapp.serializers import PurchaseSerializer
from rest_framework import generics
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
这个视图只返回当前认证用户的采购列表。
"""
user = self.request.user
return Purchase.objects.filter(purchaser=user)
在url中捕获参数:
path('purchases/<str:username>/', PurchaseList.as_view())
然后在视图中根据捕获的参数进行过滤:
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
这个视图只返回由URL的username部分所指定用户的采购列表。
"""
username = self.kwargs['username']
return Purchase.objects.filter(purchaser__username=username)
django-filter
库包含了一个DjangoFilterBackend
类,它支持DRF的高度定制字段过滤,需要我们自己安装:
pip install django-filter
注册到django项目的INSTALLED_APPS
:
INSTALLED_APPS = [
...
'django_filters',
...
]
全局设置,对所有视图都有效:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
局部设置,只对当前视图有效:
from django_filters.rest_framework import DjangoFilterBackend
class UserListView(generics.ListAPIView):
...
filter_backends = [DjangoFilterBackend]
如果只是需要简单的通过字段值是否相等进行过滤,那么就可以在视图或视图集上设置一个filterset_fields
属性,列出您希望过滤的字段集。
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['category', 'in_stock']
这将自动为给定的字段创建一个FilterSet类,并允许使用url的查询集字符串进行过滤:
http://example.com/api/products?category=clothing&in_stock=True
更加详细的说明请参考django-filter的官方文档:传送门
OrderingFilter
类支持简单的查询参数控制的结果排序。默认情况下,查询参数名为ordering
。比如:
http://example.com/api/users?ordering=username
如果想要降序,在字段名前加上-
:
http://example.com/api/users?ordering=-username
还可以指定多个字段:
http://example.com/api/users?ordering=account,username
可以通过在视图中设置ordering_fields
属性,指定允许作为排序依据的所有字段,防止敏感信息泄露:
class UserListView(generics.ListAPIView):
……
filter_backends = [filters.OrderingFilter]
ordering_fields = ['username', 'email']
如果肯定没有敏感信息,可以使用'__all__'
指定所有字段。
class BookingsListView(generics.ListAPIView):
……
filter_backends = [filters.OrderingFilter]
ordering_fields = '__all__'
ordering
属性可以是一个字符串,也可以是字符串构成的列表或元组:
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = ['username', 'email']
ordering = ['username']
同样的,需要降序可以加-
号
url中查询参数的默认名称为ordering
,我们可以通过ORDERING_PARAM
设置覆盖:
REST_FRAMEWORK = {
'ORDERING_PARAM': 'order_by',
}
DRF的视图能够处理各种异常,并返回适当的错误响应。这样能有助于我们与前端开发人员合作,统一接口,更加合理的处理这些异常。
响应内容包括状态码、内容类型和响应主体,响应主体则包含了错误更加详细的信息。比如,DELETE
方法不被允许的错误响应:
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
Content-Length: 42
{"detail": "Method 'DELETE' not allowed."}
而验证错误的处理方式略有不同,它会将验证失败的字段作为键。比如,下面amount
和description
字段的验证错误:
HTTP/1.1 400 Bad Request
Content-Type: application/json
Content-Length: 94
{"amount": ["A valid integer is required."], "description": ["This field may not be blank."]}
如果验证错误不是关于某个特定字段的(也许涉及多个字段),那么就会以non_field_errors
作为键。想要修改就要在项目配置中设置NON_FIELD_ERRORS_KEY
的值。
我们可以通过创建一个处理函数来实现自定义异常处理,该处理函数将API视图中引发的异常转换为响应对象,以便我们控制错误响应的格式。
该函数必须接受两个参数:
context['view']
可以获取当前正在处理的视图。异常处理函数要么返回一个Response
对象,要么返回None
(如果无法处理异常)。如果处理程序返回None
,那么这个异常将被重新抛出,Django将返回一个标准的 HTTP 500 ‘server error’ 响应。
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# 获取标准的错误响应,这也就是默认的异常处理器
response = exception_handler(exc, context)
"""
按照我们自己的规范,定制错误响应
这里我们在标准错误响应的基础上添加了一个状态码
"""
if response is not None:
response.data['status_code'] = response.status_code
return response
自定义的异常处理器需要进行配置,否则不会生效。
在项目配置文件中:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
如果不配置,则会使用默认的异常处理器:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。