DRF框架基本组件之认证&权限
认证
身份验证功能需要可插拔。
— Jacob Kaplan-Moss, "REST worst practices"
身份验证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制。然后,权限 和 限制 可以使用这些凭据来确定是否应允许该请求。
REST framework 提供了一些开箱即用的身份验证方案,并且还允许你实现自定义方案。
验证始终在视图的最开始进行,在执行权限和限制检查之前以及允许任何其他代码继续执行之前。
request.user
属性通常被设置为contrib.auth
包中 User
类的一个实例。
request.auth
属性用于任何其他身份验证信息,例如,它可以用于表示请求签名的身份验证令牌。
注意: 不要忘了认证本身不会允许或拒绝传入的请求,它只是简单识别请求携带的凭证。
有关如何设置API权限策略的信息,请参阅 权限文档。
如何确定身份验证
认证方案总是被定义为一个类的列表。REST framework 将尝试使用列表中的每个类进行身份验证,并使用成功完成验证的第一个类的返回值设置 request.user
和request.auth
。
如果没有类进行验证,request.user
将被设置成 django.contrib.auth.models.AnonymousUser
的实例,request.auth
将被设置成None
。
未认证请求的request.user
和 request.auth
的值可以使用 UNAUTHENTICATED_USER
和UNAUTHENTICATED_TOKEN
设置进行修改。
设置认证方案
可以使用 DEFAULT_AUTHENTICATION_CLASSES
设置全局的默认身份验证方案。比如:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ) }
你还可以使用基于APIView
类视图的方式,在每个view或每个viewset基础上设置身份验证方案。
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` 实例。 'auth': unicode(request.auth), # None } return Response(content)
或者,如果你使用基于函数的视图,那就使用@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` 实例。 'auth': unicode(request.auth), # None } return Response(content)
未认证和禁止的响应
当未经身份验证的请求被拒绝时,有下面两种不同的错误代码可使用。
- HTTP 401 未认证
- HTTP 403 无权限
HTTP 401 响应必须始终包括一个WWW-Authenticate
头,指示客户端如何进行身份验证。 HTTP 403响应不包括WWW-Authenticate
。
具体使用哪种响应取决于认证方案。虽然可以使用多种认证方案,但是仅可以使用一种方案来确定响应的类型。在确定响应类型时,将使用视图上设置的第一个认证类。
注意,当一个请求通过了验证但是被拒绝执行请求的权限时,不管认证方案是什么,都要使用 403 Permission Denied
响应。
Apache mod_wsgi 具体配置
注意,如果使用 Apache using mod_wsgi部署,认证头默认不会传递给WSGI应用程序,它假定由Apache处理认证,而不是在应用层面处理。
如果你正在部署到Apache,并且使用任何non-session的身份验证,则需要显式配置mod_wsgi才能将所需的头文件传递给应用程序。这可以通过在适当的上下文中指定WSGIPassAuthorization
指令并将其设置为'On'
来完成。
# 这可能会在服务器配置,虚拟主机,目录或.htaccess
WSGIPassAuthorization On
权限
身份验证或身份识别本身通常不足以获取信息或代码的访问权限。因此,请求访问的实体必须具有授权。
— Apple Developer Documentation
连同认证和限制,权限决定是否应该接收请求或拒绝访问。
权限检查始终在视图的开始处运行,在允许继续执行任何其他代码之前运行。 权限检查通常会使用request.user
和request.auth
属性中的身份验证信息来确定是否允许传入请求。
权限用于授予或拒绝将不同类别的用户访问到API的不同部分。
最简单的权限是允许访问任何经过身份验证的用户,并拒绝访问任何未经身份验证的用户。这对应于REST框架中的IsAuthenticated
类。
稍微严格的权限控制风格是对经过身份验证的用户的允许完全访问,但对未经身份验证的用户的允许只读访问。这对应于REST框架中的IsAuthenticatedOrReadOnly
类。
如何确定权限
REST框架中的权限始终被定义为一个权限类的列表。
在运行视图的主体之前,检查列表中的每个权限。 如果任何权限检查失败,会抛出一个exceptions.PermissionDenied
或exceptions.NotAuthenticated
异常,并且视图的主体将不会运行。
当权限检查失败时,将返回"403 Forbidden"或"401 Unauthorized"响应,具体根据以下规则:
- 请求已成功通过身份验证,但权限被拒绝。 — 将返回403 Forbidden响应。
- 请求未成功认证,最高优先级的认证类不使用
WWW-Authenticate
标头。— 将返回403 Forbidden响应。 - 请求未成功认证,最高优先级的认证类使用
WWW-Authenticate
标头。— 将返回HTTP 401未经授权的响应,并附带适当的WWW-Authenticate
标头。
对象级权限
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()) self.check_object_permissions(self.request, obj) return obj
对象级别的权限限制
出于性能原因,通用视图在返回对象列表时不会自动将对象级权限应用于查询集中的每个实例。
通常,当你使用对象级权限时,你还需要适当地过滤查询集过滤查询集,以确保用户只能看到他们被允许查看的实例。
设置权限策略
默认权限策略可以使用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文件中设置的默认列表。