Django REST framework API 指南(13):認證

官方原文連接
本系列文章 github 地址
轉載請註明出處html

身份驗證

身份驗證是將傳入請求與一組識別憑證(例如請求的用戶或其簽名的令牌)相關聯的機制。而後,權限和限制策略可使用這些憑據來肯定請求是否應該被容許。python

REST framework 提供了許多開箱即用的身份驗證方案,同時也容許你實施自定義方案。nginx

身份驗證始終在視圖的開始處運行,在執行權限和限制檢查以前,在容許繼續執行任何其餘代碼以前。git

request.user 屬性一般會設置爲 contrib.auth 包的 User 類的一個實例。github

request.auth 屬性用於其餘身份驗證信息,例如,它能夠用來表示請求已簽名的身份驗證令牌。數據庫


注意: 不要忘記, 身份驗證自己不會(容許或不容許)傳入的請求,它只是標識請求的憑據。apache


如何肯定身份驗證

認證方案老是被定義爲一個類的列表。 REST framework 將嘗試使用列表中的每一個類進行認證,並將使用成功認證的第一個類的返回值來設置 request.userrequest.authdjango

若是沒有類進行身份驗證,則將 request.user 設置爲 django.contrib.auth.models.AnonymousUser 的實例,並將 request.auth 設置爲 None.後端

可使用 UNAUTHENTICATED_USERUNAUTHENTICATED_TOKEN 設置修改未經身份驗證的請求的 request.userrequest.auth 的值。api

設置認證方案

默認的認證方案可使用 DEFAULT_AUTHENTICATION_CLASSES setting 全局設置。例如。

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)
複製代碼

未經受權和禁止響應

當未經身份驗證的請求被拒絕時,有兩種不一樣的錯誤代碼多是合適的。

  • [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' 來完成。

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

API 參考

BasicAuthentication

該認證方案使用 HTTP Basic Authentication,並根據用戶的用戶名和密碼進行簽名。Basic Authentication 一般只適用於測試。

若是成功經過身份驗證,BasicAuthentication 將提供如下憑據。

  • request.user 是一個 Django User 實力.
  • request.authNone.

未經身份驗證的響應被拒絕將致使 HTTP 401 Unauthorized 的響應和相應的 WWW-Authenticate header。例如:

WWW-Authenticate: Basic realm="api"
複製代碼

注意: 若是您在生產環境中使用 BasicAuthentication,則必須確保您的 API 僅可經過 https 訪問。您還應該確保您的 API 客戶端將始終在登陸時從新請求用戶名和密碼,而且永遠不會將這些詳細信息存儲到持久化存儲中。

TokenAuthentication

此認證方案使用簡單的基於令牌的 HTTP 認證方案。令牌身份驗證適用於 client-server 架構,例如本機桌面和移動客戶端。

要使用 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 header 中。關鍵字應以字符串文字 「Token」 爲前綴,用空格分隔兩個字符串。例如:

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。例如:

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 framework 提供了一個內置的視圖來支持這種行爲。要使用它,請將 obtain_auth_token 視圖添加到您的 URLconf 中:

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

請注意,模式的 URL 部分能夠是任何你想使用的。

當使用表單數據或 JSON 將有效的 usernamepassword 字段發佈到視圖時, obtain_auth_token 視圖將返回 JSON 響應:

{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
複製代碼

請注意,缺省的 obtain_auth_token 視圖顯式使用 JSON 請求和響應,而不是使用你設置的默認的渲染器和解析器類。

默認狀況下,沒有權限或限制應用於 obtain_auth_token 視圖。 若是您但願應用 throttling ,則須要重寫視圖類,並使用 throttle_classes 屬性包含它們。

若是你須要自定義 obtain_auth_token 視圖,你能夠經過繼承 ObtainAuthToken 視圖類來實現,並在你的 url conf 中使用它。

例如,您可能會返回超出 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 += [
    url(r'^api-token-auth/', CustomAuthToken.as_view())
]
複製代碼
使用 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 命令

從版本 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 的默認 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」 的自定義請求頭中的用戶名對任何傳入請求進行身份驗證。

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)
複製代碼
相關文章
相關標籤/搜索