authentication.py

身份验证

身份验证需要可插入。

— Jacob Kaplan-Moss,"REST 最差实践"

身份验证是将传入请求与一组标识凭证(例如请求的来源用户或用于对其进行签名的令牌)关联的机制。权限限制策略随后可以使用这些凭证来确定是否应允许请求。

REST 框架提供了多种开箱即用的身份验证方案,还允许您实施自定义方案。

身份验证总是在视图的开始阶段运行,在执行权限和限制检查之前以及在允许任何其他代码继续之前。

request.user属性通常将设置为contrib.auth包的User类的实例。

request.auth属性用于任何其他身份验证信息,例如,它可用于表示用于对请求进行签名的身份验证令牌。


注意:不要忘记身份验证本身不会允许或不允许传入请求,它只是标识请求所使用的凭证。

有关如何为 API 设置权限策略的信息,请参阅权限文档


如何确定身份验证

身份验证方案始终定义为一个类列表。REST 框架将尝试使用列表中的每个类进行身份验证,并将使用成功进行身份验证的第一个类的返回值设置request.userrequest.auth

如果没有类进行身份验证,request.user 将被设置为 django.contrib.auth.models.AnonymousUser 的一个实例,而 request.auth 将被设置为 None

对于未经身份验证的请求,request.userrequest.auth 的值可以使用 UNAUTHENTICATED_USERUNAUTHENTICATED_TOKEN 设置进行修改。

设置身份验证方案

可以使用 DEFAULT_AUTHENTICATION_CLASSES 设置在全局范围内设置默认身份验证方案。例如。

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]
}

你还可以使用基于类的视图 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': str(request.user),  # `django.contrib.auth.User` instance.
            'auth': str(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': str(request.user),  # `django.contrib.auth.User` instance.
        'auth': str(request.auth),  # None
    }
    return Response(content)

未授权和禁止的响应

当未经身份验证的请求被拒绝权限时,可能有两种不同的错误代码是合适的。

HTTP 401 响应必须始终包含一个 WWW-Authenticate 标头,该标头指示客户端如何进行身份验证。HTTP 403 响应不包含 WWW-Authenticate 标头。

将使用哪种响应取决于身份验证方案。虽然可以使用多个身份验证方案,但只能使用一个方案来确定响应类型。在视图中设置的第一个身份验证类用于确定响应类型

请注意,当请求可能成功进行身份验证,但仍被拒绝执行请求的权限时,在这种情况下,将始终使用 403 拒绝访问 响应,而不管身份验证方案如何。

Apache mod_wsgi 特定配置

请注意,如果部署到 使用 mod_wsgi 的 Apache,则默认情况下不会将授权标头传递到 WSGI 应用程序,因为假定身份验证将由 Apache 处理,而不是在应用程序级别处理。

如果你正在部署到 Apache,并使用任何基于非会话的身份验证,则需要明确配置 mod_wsgi 以将必需的标头传递到应用程序。这可以通过在适当的上下文中指定 WSGIPassAuthorization 指令并将其设置为 'On' 来完成。

# this can go in either server config, virtual host, directory or .htaccess
WSGIPassAuthorization On

API 参考

BasicAuthentication

此身份验证方案使用 HTTP 基本身份验证,针对用户的用户名和密码进行签名。基本身份验证通常仅适用于测试。

如果成功进行身份验证,BasicAuthentication 将提供以下凭据。

  • request.user 将成为 Django User 实例。
  • request.auth 将为 None

拒绝权限的未经身份验证的响应将导致 HTTP 401 Unauthorized 响应,并带有适当的 WWW-Authenticate 标头。例如

WWW-Authenticate: Basic realm="api"

注意:如果您在生产中使用 BasicAuthentication,则必须确保您的 API 仅通过 https 提供。您还应确保您的 API 客户端在登录时始终重新请求用户名和密码,并且永远不会将这些详细信息存储到持久性存储中。

TokenAuthentication


注意:Django REST 框架提供的令牌身份验证是一个相当简单的实现。

对于允许每个用户使用多个令牌、具有更严格的安全实现细节并支持令牌过期的实现,请参阅 Django REST Knox 第三方包。


此身份验证方案使用基于简单令牌的 HTTP 身份验证方案。令牌身份验证适用于客户端-服务器设置,例如本机桌面和移动客户端。

要使用 TokenAuthentication 方案,您需要 配置身份验证类 以包括 TokenAuthentication,此外还要在 INSTALLED_APPS 设置中包括 rest_framework.authtoken

INSTALLED_APPS = [
    ...
    'rest_framework.authtoken'
]

更改设置后,请务必运行 manage.py migrate

rest_framework.authtoken 应用程序提供 Django 数据库迁移。

您还需要为您的用户创建令牌。

from rest_framework.authtoken.models import Token

token = Token.objects.create(user=...)
print(token.key)

对于客户端进行身份验证,令牌密钥应包含在 Authorization HTTP 标头中。密钥应以字符串文字“Token”为前缀,两个字符串之间用空格分隔。例如

Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

如果您想在标头中使用不同的关键字,例如 Bearer,只需子类化 TokenAuthentication 并设置 keyword 类变量。

如果成功通过身份验证,TokenAuthentication 将提供以下凭据。

  • request.user 将成为 Django User 实例。
  • request.auth 将成为 rest_framework.authtoken.models.Token 实例。

拒绝权限的未经身份验证的响应将导致 HTTP 401 Unauthorized 响应,并带有适当的 WWW-Authenticate 标头。例如

WWW-Authenticate: Token

curl 命令行工具可能对测试令牌身份验证的 API 有用。例如

curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'

注意:如果您在生产中使用 TokenAuthentication,则必须确保您的 API 仅通过 https 提供。


生成令牌

通过使用信号

如果您希望每个用户都拥有自动生成的令牌,则可以简单地捕获用户的 post_save 信号。

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)

请注意,您需要确保将此代码段放在已安装的 models.py 模块或 Django 在启动时将导入的其他位置中。

如果您已经创建了一些用户,则可以像这样为所有现有用户生成令牌

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 端点

在使用 TokenAuthentication 时,您可能希望为客户端提供一种获取给定用户名和密码的令牌的机制。REST 框架提供了一个内置视图来提供此行为。要使用它,请将 obtain_auth_token 视图添加到您的 URLconf

from rest_framework.authtoken import views
urlpatterns += [
    path('api-token-auth/', views.obtain_auth_token)
]

请注意,模式的 URL 部分可以是您想要使用的任何内容。

当使用表单数据或 JSON 将有效的 usernamepassword 字段 POST 到视图时,obtain_auth_token 视图将返回 JSON 响应

{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }

请注意,默认的 obtain_auth_token 视图明确使用 JSON 请求和响应,而不是在您的设置中使用默认的渲染器和解析器类。

默认情况下,不会对 obtain_auth_token 视图应用任何权限或节流。如果您确实希望应用节流,则需要覆盖视图类,并使用 throttle_classes 属性包含它们。

如果您需要 obtain_auth_token 视图的自定义版本,则可以通过子类化 ObtainAuthToken 视图类并在 URL 配置中使用它来实现。

例如,您可能会返回 token 值之外的其他用户信息

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
        })

并在您的 urls.py

urlpatterns += [
    path('api-token-auth/', CustomAuthToken.as_view())
]

使用 Django 管理

还可以通过管理界面手动创建令牌。如果您使用的是大型用户群,我们建议您对 TokenAdmin 类进行猴子补丁,以根据您的需要对其进行自定义,更具体地说,通过将 user 字段声明为 raw_field

your_app/admin.py:

from rest_framework.authtoken.admin import TokenAdmin

TokenAdmin.raw_id_fields = ['user']

使用 Django manage.py 命令

从 3.6.4 版本开始,可以使用以下命令生成用户令牌

./manage.py drf_create_token <username>

此命令将返回给定用户的 API 令牌,如果不存在则创建它

Generated token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b for user user1

如果您想重新生成令牌(例如,如果它已被泄露),则可以传递一个附加参数

./manage.py drf_create_token -r <username>

SessionAuthentication

此身份验证方案使用 Django 的默认会话后端进行身份验证。会话身份验证适用于在与您的网站相同的会话上下文中运行的 AJAX 客户端。

如果成功认证,SessionAuthentication 将提供以下凭据。

  • request.user 将成为 Django User 实例。
  • request.auth 将为 None

拒绝访问权限的未经认证的响应将导致 HTTP 403 Forbidden 响应。

如果您将 SessionAuthentication 与 AJAX 风格的 API 一起使用,您需要确保为任何“不安全”HTTP 方法调用(例如 PUTPATCHPOSTDELETE 请求)包含一个有效的 CSRF 令牌。有关更多详细信息,请参阅 Django CSRF 文档

警告:创建登录页面时,务必使用 Django 的标准登录视图。这将确保您的登录视图得到妥善保护。

REST 框架中的 CSRF 验证与标准 Django 的工作方式略有不同,这是因为需要对同一个视图支持基于会话和非会话的身份验证。这意味着只有经过身份验证的请求才需要 CSRF 令牌,并且可以发送匿名请求而无需 CSRF 令牌。此行为不适用于登录视图,该视图应始终应用 CSRF 验证。

RemoteUserAuthentication

此身份验证方案允许您将身份验证委派给您的 Web 服务器,该服务器设置 REMOTE_USER 环境变量。

要使用它,您必须在 AUTHENTICATION_BACKENDS 设置中拥有 django.contrib.auth.backends.RemoteUserBackend(或子类)。默认情况下,RemoteUserBackend 为尚不存在的用户名创建 User 对象。要更改此行为和其他行为,请查阅 Django 文档

如果成功认证,RemoteUserAuthentication 将提供以下凭据

  • request.user 将成为 Django User 实例。
  • request.auth 将为 None

查阅您的 Web 服务器文档以获取有关配置身份验证方法的信息,例如

自定义身份验证

要实现自定义身份验证方案,请对 BaseAuthentication 进行子类化并覆盖 .authenticate(self, request) 方法。如果身份验证成功,该方法应返回 (user, auth) 的二元组,否则返回 None

在某些情况下,您可能希望从 .authenticate() 方法引发 AuthenticationFailed 异常,而不是返回 None

您通常应采取的方法是

  • 如果未尝试身份验证,请返回 None。任何其他正在使用的身份验证方案仍将被检查。
  • 如果尝试身份验证但失败,则引发 AuthenticationFailed 异常。无论任何权限检查,也不检查任何其他身份验证方案,都将立即返回错误响应。

还可以覆盖 .authenticate_header(self, request) 方法。如果已实现,它应返回一个字符串,该字符串将用作 HTTP 401 Unauthorized 响应中 WWW-Authenticate 标头的值。

如果未覆盖 .authenticate_header() 方法,则当拒绝未经身份验证的请求访问时,身份验证方案将返回 HTTP 403 Forbidden 响应。


注意:当请求对象的 .user.auth 属性调用您的自定义验证器时,您可能会看到 AttributeError 重新引发为 WrappedAttributeError。这对于防止外部属性访问压制原始异常是必需的。Python 不会识别 AttributeError 源自您的自定义验证器,而是会假设请求对象没有 .user.auth 属性。这些错误应由您的验证器修复或以其他方式处理。


示例

以下示例将对传入的任何请求进行身份验证,该请求是名为“X-USERNAME”的自定义请求标头中用户名给出的用户。

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('HTTP_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)

第三方包

以下第三方包也可用。

django-rest-knox

Django-rest-knox 库提供模型和视图,以比内置 TokenAuthentication 方案更安全、更可扩展的方式处理基于令牌的身份验证 - 考虑单页应用程序和移动客户端。它提供每个客户端的令牌,以及在提供其他一些身份验证(通常是基本身份验证)时生成令牌的视图,以删除令牌(提供服务器强制注销)并删除所有令牌(注销用户登录的所有客户端)。

Django OAuth Toolkit

Django OAuth Toolkit 包提供 OAuth 2.0 支持,并适用于 Python 3.4+。该包由 jazzband 维护,并使用出色的 OAuthLib。该包有很好的文档记录,并得到很好的支持,目前是我们的OAuth 2.0 支持推荐包

安装和配置

使用 pip 安装。

pip install django-oauth-toolkit

将包添加到 INSTALLED_APPS 并修改 REST 框架设置。

INSTALLED_APPS = [
    ...
    'oauth2_provider',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
    ]
}

有关更多详细信息,请参阅 Django REST 框架 - 入门 文档。

Django REST framework OAuth

Django REST 框架 OAuth 包为 REST 框架提供了 OAuth1 和 OAuth2 支持。

此包以前直接包含在 REST 框架中,但现在作为第三方包提供支持和维护。

安装和配置

使用 pip 安装包。

pip install djangorestframework-oauth

有关配置和使用详情,请参阅 Django REST 框架 OAuth 文档,了解 身份验证权限

JSON Web Token Authentication

JSON Web 令牌是一个相当新的标准,可用于基于令牌的身份验证。与内置的 TokenAuthentication 方案不同,JWT 身份验证不需要使用数据库来验证令牌。JWT 身份验证的包是 djangorestframework-simplejwt,它提供了一些功能以及可插入的令牌黑名单应用。

Hawk HTTP Authentication

HawkREST 库建立在 Mohawk 库之上,让你可以在 API 中使用 Hawk 签名请求和响应。 Hawk 允许两方使用由共享密钥签名的消息安全地相互通信。它基于 HTTP MAC 访问身份验证(基于 OAuth 1.0 的部分内容)。

HTTP Signature Authentication

HTTP 签名(目前是 IETF 草案)提供了一种实现 HTTP 消息的原点身份验证和消息完整性的方法。类似于 亚马逊的 HTTP 签名方案(其许多服务使用),它允许无状态的、按请求进行身份验证。 Elvio Toccalino 维护 djangorestframework-httpsignature(已过时)包,该包提供了一种易于使用的 HTTP 签名身份验证机制。你可以使用 djangorestframework-httpsignature 的更新的 fork 版本,即 drf-httpsig

Djoser

Djoser 库提供了一组视图来处理基本操作,例如注册、登录、注销、密码重置和帐户激活。该包使用自定义用户模型并使用基于令牌的身份验证。这是 Django 身份验证系统的一个可立即使用的 REST 实现。

django-rest-auth / dj-rest-auth

此库提供了一组用于注册、认证(包括社交媒体认证)、密码重置、检索和更新用户信息等的 REST API 端点。通过拥有这些 API 端点,您的客户端应用(如 AngularJS、iOS、Android 等)可以通过 REST API 独立地与您的 Django 后端网站进行通信,以进行用户管理。

此项目目前有两个分支。

drf-social-oauth2

Drf-social-oauth2 是一个框架,可帮助您通过主要的社交 oauth2 供应商(如 Facebook、Google、Twitter、Orcid 等)进行认证。它以 JWTed 方式生成令牌,设置简单。

drfpasswordless

drfpasswordless 为 Django REST Framework 的 TokenAuthentication 方案添加了(Medium、Square Cash 启发的)无密码支持。用户使用发送到电子邮件地址或手机号码等联系点的令牌登录和注册。

django-rest-authemail

django-rest-authemail 为用户注册和认证提供了一个 RESTful API 接口。电子邮件地址用于认证,而不是用户名。API 端点可用于注册、注册电子邮件验证、登录、注销、密码重置、密码重置验证、电子邮件更改、电子邮件更改验证、密码更改和用户详细信息。包括一个功能齐全的示例项目和详细说明。

Django-Rest-Durin

Django-Rest-Durin 的构建理念是拥有一个库,通过一个界面为多个 Web/CLI/移动 API 客户端执行令牌认证,但允许每个使用 API 的 API 客户端进行不同的令牌配置。它通过自定义模型、视图和与 Django-Rest-Framework 配合使用的权限提供对每个用户多个令牌的支持。令牌到期时间可以因 API 客户端而异,并且可以通过 Django 管理界面进行自定义。

更多信息请参见文档