Django REST framework 源码剖析-认证器详解(Authentication)
身份验证始终在视图的最开始运行,在权限和限制检查发生之前,以及在允许任何其他代码继续之前。
request.user属性通常设置为contrib.auth包的user类的实例。
request.auth属性用于任何其他身份验证信息,例如,它可以用来表示请求签名时使用的身份验证令牌。
配置全局认证器
python
复制代码
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
}
基于 APIView 类的视图
python
复制代码
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)
基于函数视图的 @api_view 装饰器
python
复制代码
@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)
未经授权和禁止响应 (Unauthorized and Forbidden responses)
当未经验证的请求被拒绝许可时,有两种不同的错误代码可能是适当的。
HTTP 401 响应必须始终包含 WWW-Authenticate 标头,指示客户端如何进行身份验证。
HTTP 403 响应不包括 WWW-Authenticate 标头。
python
复制代码
HTTP 401 未经授权
HTTP 403 权限被拒绝
API参考
BasicAuthentication
此身份验证方案使用 HTTP 基本身份验证,对用户的用户名和密码进行签名。基本身份验证通常仅适用于测试。
如果您在产品中使用 BasicAuthentication,您必须确保您的 API 仅在 https 上可用。您还应确保您的 API 客户端始终在登录时重新请求用户名和密码,并且不会存储这些详细信息到持久存储器中
成功 提供以下凭据
python
复制代码
request.user 将是 Django User 实例。
request.auth 将是 None。
python
复制代码
WWW-Authenticate: Basic realm="api"
TokenAuthentication
此身份验证方案使用简单的基于令牌的 HTTP 身份验证方案。令牌认证适用于客户端-服务器设置,例如本地桌面和移动客户端。
如果您在产品中使用 TokenAuthentication,您必须确保您的 API 仅在 https 上可用
使用TokenAuthentication认证方式,需要提供以下配置
python
复制代码
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
python
复制代码
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=...)
print token.key
python
复制代码
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
如果想要在标头中使用不同的关键字,例如 Bearer,只需将 TokenAuthentication 子类化并设置 keyword 类变量
成功通过身份验证,TokenAuthentication 提供以下凭据。
python
复制代码
request.user 将是 Django User 实例。
request.auth 将是 rest_framework.authtoken.models.Token 实例。
拒绝许可的未经身份验证的响应将导致带有相应的 WWW-Authenticate 标头的 HTTP 401 Unauthorized 响应
python
复制代码
WWW-Authenticate: Token
bash
复制代码
curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'
生成令牌 (Generating Tokens)
通过使用信号 (By using signals)
如果您想要每个用户都拥有一个自动生成的令牌,您只需捕获用户的 post_save 信号即可
python
复制代码
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
如果您已经创建了一些用户,则可以为所有现有用户生成令牌,如下所示
python
复制代码
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
for user in User.objects.all():
Token.objects.get_or_create(user=user)
通过暴露 api 端点 (By exposing an api endpoint)
当使用 TokenAuthentication 时,你可能希望为客户端提供一种获取给定用户名和密码的令牌的机制。REST framework 提供了一个内置的视图来提供此行为
默认的 obtain_auth_token 视图显式使用 JSON 请求和响应,而不是使用 settings 中默认的渲染器和解析器类
将 obtain_auth_token 视图添加到你的 URLconf
python
复制代码
from rest_framework.authtoken import views
urlpatterns += [
url(r'^api-token-auth/', views.obtain_auth_token)
]
如果需要自定义版本的 obtain_auth_token 视图,可以通过子类化 ObtainAuthToken 类,并在 url conf 中使用它来实现
python
复制代码
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email
})
python
复制代码
urlpatterns += [
url(r'^api-token-auth/', CustomAuthToken.as_view())
]
通过Django admin管理token
xxx_app/admin.py
python
复制代码
from rest_framework.authtoken.admin import TokenAdmin
TokenAdmin.raw_id_fields = ('user',)
使用 Django manage.py 命令 (Using Django manage.py command)
python
复制代码
./manage.py drf_create_token <username>
此命令将返回给定用户的 API 令牌,如果它不存在则创建它
python
复制代码
Generated token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b for user user1
如果您想要重新生成令牌 (例如如果它已被破坏或泄露),您可以传递一个额外的参数
python
复制代码
./manage.py drf_create_token -r <username>
SessionAuthentication
此身份验证方案使用 Django 的默认会话后端进行身份验证。会话身份验证适用于与您的网站在同一会话上下文中运行的 AJAX 客户端。
成功
python
复制代码
request.user 是 Django User 实例。
request.auth 是 None。
失败则返回, 未经许可的未经身份验证的响应将导致 HTTP 403 Forbidden 响应
RemoteUserAuthentication
此身份验证方案允许您将身份验证委托给 Web 服务器,该服务器设置 REMOTE_USER 环境变量。
要使用它,你必须在你的 AUTHENTICATION_BACKENDS 设置中有 django.contrib.auth.backends.RemoteUserBackend (或者子类)
默认情况下,RemoteUserBackend 为尚不存在的用户名创建 User 对象, 要更改此行为和其他行为,请参阅 Django 文档。
成功通过身份验证,RemoteUserAuthentication 提供以下凭据:
python
复制代码
request.user 是 Django User 实例。
request.auth 是 None。
失败则返回, 未经许可的未经身份验证的响应将导致 HTTP 403 Forbidden 响应
自定义身份验证 (Custom authentication)
要实现自定义的身份验证方案,要继承 BaseAuthentication 类并且重写 .authenticate(self, request) 方法。如果认证成功,该方法应返回 (user, auth) 的二元元组,否则返回 None
在某些情况下,您可能想要从 .authenticate() 方法引发 AuthenticationFailed 异常,而不是返回 None。
通常,您应采取的方法是:
python
复制代码
- 如果未尝试验证,返回 None。还将检查任何其他正在使用的身份验证方案。
- 如果尝试验证失败,则引发 AuthenticationFailed 异常。无论是否进行任何权限检查,都将立即返回错误响应,并且不会检查任何其他身份验证方案。
重写 .authenticate_header(self, request) 方法。如果实现,则应返回将用作 HTTP 401 Unauthorized 响应中的 WWW-Authenticate 标头的值的字符串
如果 .authenticate_header() 方法未被重写,则身份验证方案将在未经验证的请求被拒绝访问时返回 HTTP 403 Forbidden 响应。
自定义请求标头中名为 'X_USERNAME' 的用户名指定的用户的任何传入请求进行身份验证
python
复制代码
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)