Authentication

认证 (Authentication)

身份验证需要可插拔。—— Jacob Kaplan-Moss, “REST worst practices”

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

REST framework 提供了一些开箱即用的身份验证方案,并且还允许您实现自定义方案。

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

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

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


注意:不要忘记,身份验证本身不会允许或不允许传入的请求,它只识别发出请求的凭证。

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


如何确定身份验证 (How authentication is determined)

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

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

可以使用 UNAUTHENTICATED_USERUNAUTHENTICATED_TOKEN 的设置修改未经身份认证的请求的 request.userrequest.auth 的值。

设置身份验证方案 (Setting the authentication scheme)

可以使用 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': unicode(request.user),  # `django.contrib.auth.User` instance.
            '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` instance.
        'auth': unicode(request.auth),  # None
    }
    return Response(content)

未经授权和禁止响应 (Unauthorized and Forbidden responses)

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

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

使用的响应类型取决于身份验证方案。尽管可以使用多种认证方案,但是仅可以使用一种方案来确定响应的类型。在确定响应类型时使用视图上设置的第一个身份验证类。

注意,当请求可以成功进行身份验证时,但仍然被拒绝执行该请求的许可,不管认证方案如何,总是使用 403 Permission Denied 响应。

Apache mod_wsgi 具体配置 (Apache mod_wsgi specific configuration)

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

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

# 这可以在服务器配置、虚拟主机、目录或 .htaccess 中执行
WSGIPassAuthorization On

API 参考 (API Reference)

BasicAuthentication

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

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

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

拒绝许可的未经身份验证的响应将导致带有相应的 WWW-Authenticate 标头的 HTTP 401 Unauthorized 响应。例如:

WWW-Authenticate: Basic realm="api"

注意:如果您在产品中使用 BasicAuthentication,您必须确保您的 API 仅在 https 上可用。您还应确保您的 API 客户端始终在登录时重新请求用户名和密码,并且不会存储这些详细信息到持久存储器中。

TokenAuthentication

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

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

INSTALLED_APPS = (
    ...
    'rest_framework.authtoken'
)

注意:确保在更改设置后运行 manage.py migraterest_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 实例。

拒绝许可的未经身份验证的响应将导致带有相应的 WWW-Authenticate 标头的 HTTP 401 Unauthorized 响应。例如:

WWW-Authenticate: Token

curl 命令行工具可用于测试令牌认证 API。例如:

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

注意:如果您在产品中使用 TokenAuthentication,您必须确保您的 API 仅在 https 上可用。


生成令牌 (Generating Tokens)

通过使用信号 (By using signals)

如果您想要每个用户都拥有一个自动生成的令牌,您只需捕获用户的 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 端点 (By exposing an api endpoint)

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

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

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

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

{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }

请注意,默认的 obtain_auth_token 视图显式使用 JSON 请求和响应,而不是使用 settings 中默认的渲染器和解析器类。

默认情况下,没有应用于 obtain_auth_token 视图的权限或限制。如果您希望应用限制,则需要重写视图类,并使用 throttle_classes 属性将它们包括在内。

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

例如,您可以返回令牌值之外的其他用户信息:

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 += [
    url(r'^api-token-auth/', CustomAuthToken.as_view())
]
With Django admin

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

your_app/admin.py

from rest_framework.authtoken.admin import TokenAdmin

TokenAdmin.raw_id_fields = ('user',)

使用 Django manage.py 命令 (Using Django manage.py command)

从版本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.authNone

未经许可的未经身份验证的响应将导致 HTTP 403 Forbidden 响应。

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

警告:创建登录页面时,始终使用 Django 的标准登录视图。这将确保您的登录视图得到适当保护。

REST framework 中的 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.authNone

有关配置身份验证方法的信息,请参阅 Web 服务器的文档,例如:

自定义身份验证 (Custom authentication)

要实现自定义的身份验证方案,要继承 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('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)

第三方包 (Third party packages)

以下是可用的第三方包。

Django OAuth Toolkit

Django OAuth Toolkit 包提供了 OAuth 2.0 支持,并且兼容 Python 2.7 和 Python 3.3+。这个包使用优秀的 OAuthLib,由 Evonove 维护。该软件包有很完善的文档,并得到很好的支持,目前是我们推荐使用的 OAuth 2.0 支持软件包。

安装和配置 (Installation & configuration)

使用 pip 安装。

pip install django-oauth-toolkit

把这个包添加到你的 INSTALLED_APPS 中,并修改您的 REST framework 设置。

INSTALLED_APPS = (
    ...
    'oauth2_provider',
)

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
    )
}

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

Django REST framework OAuth

Django REST framework OAuth 包为 REST framework 提供 OAuth1 和 OAuth2 支持。

此软件包以前直接包含在 REST framework 包中,但现在作为第三方包支持和维护。

安装和配置 (Installation & configuration)

使用 pip 安装软件包。

pip install djangorestframework-oauth

有关配置和使用的详细信息,请参阅 Django REST framework OAuth 文档的身份验证权限

Digest Authentication

HTTP 摘要认证是一种广泛实施的方案,旨在取代 HTTP 基本认证,并提供简单的加密认证机制。Juan Riaza 维护着 djangorestframework-digestauth 包,它为 REST framework 提供 HTTP 摘要认证支持。

Django OAuth2 Consumer

Rediker SoftwareDjango OAuth2 Consumer 是另一个为 REST framework 提供 OAuth 2.0 支持的软件包。该软件包包含令牌的令牌范围权限,允许对你的 API 进行更细粒度的访问。

JSON Web Token Authentication

JSON Web Token 是一个相当新的标准,可用于基于令牌的身份验证。与内置 TokenAuthentication 方案不同,JWT Authentication 不需要使用数据库来验证令牌。Blimp 维护着 djangorestframework-jwt 软件包,它提供了 JWT Authentication 类,以及客户端获取给定用户名和密码的 JWT 的机制。JWT 身份验证的另一个包是 djangorestframe -simplejwt,它提供了不同的特性以及可插入的令牌黑名单应用程序。

Hawk HTTP Authentication

HawkREST 库以 Mohawk 库为基础,允许您在 API 中处理 Hawk 签名的请求和响应。Hawk 让双方使用共享密钥签名的消息彼此安全地进行通信。它基于 HTTP MAC 访问认证(基于 OAuth 1.0 的部分)。

HTTP Signature Authentication

HTTP 签名(目前为 IETF 草案)提供了一种实现 HTTP 消息的源认证和消息完整性的方法。与 Amazon 的 HTTP 签名方案类似,许多服务使用它,它允许无状态的每个请求的身份验证。Elvio Toccalino 维护 djangorestframework-httpsignature 包,提供了一个易于使用的 HTTP 签名身份验证机制。

Djoser

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

django-rest-auth

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

django-rest-framework-social-oauth2

Django-rest-framework-social-oauth2 库提供了一种将社交插件(facebook、twitter、google 等)集成到您的身份验证系统和简单的 oauth2 设置的简单方法。使用这个库,您将能够基于外部令牌(例如 facebook 访问令牌)对用户进行身份验证,将这些令牌转换为“内部” oauth2 令牌,并使用和生成 oauth2 令牌来验证您的用户。

django-rest-knox

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

drfpasswordless

drfpasswordless 为 Django REST Framework 自己的 TokenAuthentication 方案添加了(Medium,Square Cash灵感)无密码支持。用户登录并使用发送到联系人点(如电子邮件地址或手机号码)的令牌进行注册。

猜你喜欢

转载自blog.csdn.net/Fweiren/article/details/85063161