官方原文链接
前往掘金阅读

身份验证

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

REST framework 提供了许多开箱即用的身份验证方案,同时也允许你实施自定义方案。

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

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

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


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


如何确定身份验证

认证方案总是被定义为一个类的列表。 REST framework 将尝试使用列表中的每个类进行认证,并将使用成功认证的第一个类的返回值来设置 request.userrequest.auth

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

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

设置认证方案

默认的认证方案可以使用 DEFAULT_AUTHENTICATION_CLASSES setting 全局设置。例如。

  1. REST_FRAMEWORK = {
  2. 'DEFAULT_AUTHENTICATION_CLASSES': (
  3. 'rest_framework.authentication.BasicAuthentication',
  4. 'rest_framework.authentication.SessionAuthentication',
  5. )
  6. }

您还可以使用基于 APIView 类的视图,在每个视图或每个视图集的基础上设置身份验证方案。

  1. from rest_framework.authentication import SessionAuthentication, BasicAuthentication
  2. from rest_framework.permissions import IsAuthenticated
  3. from rest_framework.response import Response
  4. from rest_framework.views import APIView
  5. class ExampleView(APIView):
  6. authentication_classes = (SessionAuthentication, BasicAuthentication)
  7. permission_classes = (IsAuthenticated,)
  8. def get(self, request, format=None):
  9. content = {
  10. 'user': unicode(request.user), # `django.contrib.auth.User` instance.
  11. 'auth': unicode(request.auth), # None
  12. }
  13. return Response(content)

或者,如果您将 @api_view 装饰器与基于函数的视图一起使用。

  1. @api_view(['GET'])
  2. @authentication_classes((SessionAuthentication, BasicAuthentication))
  3. @permission_classes((IsAuthenticated,))
  4. def example_view(request, format=None):
  5. content = {
  6. 'user': unicode(request.user), # `django.contrib.auth.User` instance.
  7. 'auth': unicode(request.auth), # None
  8. }
  9. return Response(content)

未经授权和禁止响应

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

  • [HTTP 401 Unauthorized][http401]
  • [HTTP 403 Permission Denied][http403]

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

将使用哪种响应取决于认证方案。尽管可能正在使用多种认证方案,但只能使用一种方案来确定响应的类型。 在确定响应类型时使用视图上设置的第一个认证类

请注意,当请求可以成功进行身份验证时,仍然可能会因为权限而被拒绝,在这种情况下,将始终使用 403 Permission Denied 响应,而不管身份验证方案如何。

Apache mod_wsgi 特定的配置

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

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

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

API 参考

BasicAuthentication

该认证方案使用 HTTP Basic Authentication,并根据用户的用户名和密码进行签名。Basic Authentication 通常只适用于测试。

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

  • request.user 是一个 Django User 实力.
  • request.authNone.

未经身份验证的响应被拒绝将导致 HTTP 401 Unauthorized 的响应和相应的 WWW-Authenticate header。例如:

  1. WWW-Authenticate: Basic realm="api"

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

TokenAuthentication

此认证方案使用简单的基于令牌的 HTTP 认证方案。令牌身份验证适用于 client-server 架构,例如本机桌面和移动客户端。

要使用 TokenAuthentication 方案,您需要将认证类配置为包含 TokenAuthentication ,并在 INSTALLED_APPS 设置中另外包含 rest_framework.authtoken

  1. INSTALLED_APPS = (
  2. ...
  3. 'rest_framework.authtoken'
  4. )

注意: 确保在更改设置后运行 manage.py migraterest_framework.authtoken 应用程序提供 Django 数据库迁移。


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

  1. from rest_framework.authtoken.models import Token
  2. token = Token.objects.create(user=...)
  3. print token.key

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

  1. Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

注意: 如果您想在 header 中使用不同的关键字(例如 Bearer),只需子类化 TokenAuthentication 并设置 keyword 类变量。

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

  • request.user 是一个 Django User 实例.
  • request.auth 是一个 rest_framework.authtoken.models.Token 实例.

未经身份验证的响应被拒绝将导致 HTTP 401 Unauthorized 的响应和相应的 WWW-Authenticate header。例如:

  1. WWW-Authenticate: Token

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

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

注意: 如果您在生产中使用 TokenAuthentication,则必须确保您的 API 只能通过 https 访问。


生成令牌

通过使用信号

如果您希望每个用户都拥有一个自动生成的令牌,则只需捕捉用户的 post_save 信号即可。

  1. from django.conf import settings
  2. from django.db.models.signals import post_save
  3. from django.dispatch import receiver
  4. from rest_framework.authtoken.models import Token
  5. @receiver(post_save, sender=settings.AUTH_USER_MODEL)
  6. def create_auth_token(sender, instance=None, created=False, **kwargs):
  7. if created:
  8. Token.objects.create(user=instance)

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

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

  1. from django.contrib.auth.models import User
  2. from rest_framework.authtoken.models import Token
  3. for user in User.objects.all():
  4. Token.objects.get_or_create(user=user)
通过暴露一个 API 端点

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

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

请注意,模式的 URL 部分可以是任何你想使用的。

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

  1. { 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }

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

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

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

例如,您可能会返回超出 token 值的其他用户信息:

  1. from rest_framework.authtoken.views import ObtainAuthToken
  2. from rest_framework.authtoken.models import Token
  3. from rest_framework.response import Response
  4. class CustomAuthToken(ObtainAuthToken):
  5. def post(self, request, *args, **kwargs):
  6. serializer = self.serializer_class(data=request.data,
  7. context={'request': request})
  8. serializer.is_valid(raise_exception=True)
  9. user = serializer.validated_data['user']
  10. token, created = Token.objects.get_or_create(user=user)
  11. return Response({
  12. 'token': token.key,
  13. 'user_id': user.pk,
  14. 'email': user.email
  15. })

还有 urls.py:

  1. urlpatterns += [
  2. url(r'^api-token-auth/', CustomAuthToken.as_view())
  3. ]
使用 Django admin

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

your_app/admin.py:

  1. from rest_framework.authtoken.admin import TokenAdmin
  2. TokenAdmin.raw_id_fields = ('user',)

使用 Django manage.py 命令

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

  1. ./manage.py drf_create_token <username>

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

  1. Generated token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b for user user1

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

  1. ./manage.py drf_create_token -r <username>

SessionAuthentication

此认证方案使用 Django 的默认 session 后端进行认证。Session 身份验证适用于与您的网站在同一会话环境中运行的 AJAX 客户端。

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

  • request.user 是一个 Django User 实例.
  • request.authNone.

未经身份验证的响应被拒绝将导致 HTTP 403 Forbidden 响应。

如果您在 SessionAuthentication 中使用 AJAX 风格的 API,则需要确保为任何 “不安全” 的 HTTP 方法调用(例如 PUTPATCHPOSTDELETE 请求)包含有效的 CSRF 令牌。

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

REST framework 中的 CSRF 验证与标准 Django 略有不同,因为需要同时支持基于 session 和非基于 session 的身份验证。这意味着只有经过身份验证的请求才需要 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 服务器的文档,例如:

自定义身份认证

要实现自定义身份验证方案,请继承 BaseAuthentication 并重写 .authenticate(self, request) 方法。如果认证成功,该方法应返回 (user, auth) 的二元组,否则返回 None

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

通常你应该采取的方法是:

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

您也 可以 重写 .authenticate_header(self, request) 方法。如果实现,它应该返回一个字符串,该字符串将用作 HTTP 401 Unauthorized 响应中的 WWW-Authenticate header 的值。

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


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


举个栗子

下面的示例将根据名为 “X_USERNAME” 的自定义请求头中的用户名对任何传入请求进行身份验证。

  1. from django.contrib.auth.models import User
  2. from rest_framework import authentication
  3. from rest_framework import exceptions
  4. class ExampleAuthentication(authentication.BaseAuthentication):
  5. def authenticate(self, request):
  6. username = request.META.get('X_USERNAME')
  7. if not username:
  8. return None
  9. try:
  10. user = User.objects.get(username=username)
  11. except User.DoesNotExist:
  12. raise exceptions.AuthenticationFailed('No such user')
  13. return (user, None)