身份驗證
身份驗證是將傳入請求與一組識別憑證(例如請求的使用者或其簽名的令牌)相關聯的機制。然後,許可權和限制策略可以使用這些憑據來確定請求是否應該被允許。
REST framework 提供了許多開箱即用的身份驗證方案,同時也允許你實施自定義方案。
身份驗證始終在檢視的開始處執行,在執行許可權和限制檢查之前,在允許繼續執行任何其他程式碼之前。
request.user
屬性通常會設定為 contrib.auth
包的 User
類的一個例項。
request.auth
屬性用於其他身份驗證資訊,例如,它可以用來表示請求已簽名的身份驗證令牌。
注意: 不要忘記, 身份驗證本身不會(允許或不允許)傳入的請求,它只是標識請求的憑據。
如何確定身份驗證
認證方案總是被定義為一個類的列表。 REST framework 將嘗試使用列表中的每個類進行認證,並將使用成功認證的第一個類的返回值來設定 request.user
和 request.auth
。
如果沒有類進行身份驗證,則將 request.user
設定為 django.contrib.auth.models.AnonymousUser
的例項,並將 request.auth
設定為 None
.
可以使用 UNAUTHENTICATED_USER
和 UNAUTHENTICATED_TOKEN
設定修改未經身份驗證的請求的 request.user
和 request.auth
的值。
設定認證方案
預設的認證方案可以使用 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
是一個 DjangoUser
實力.request.auth
是None
.
未經身份驗證的響應被拒絕將導致 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 migrate
。 rest_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
是一個 DjangoUser
例項.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 將有效的 username
和 password
欄位釋出到檢視時, 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
是一個 DjangoUser
例項.request.auth
是None
.
未經身份驗證的響應被拒絕將導致 HTTP 403 Forbidden
響應。
如果您在 SessionAuthentication 中使用 AJAX 風格的 API,則需要確保為任何 “不安全” 的 HTTP 方法呼叫(例如 PUT
,PATCH
,POST
或 DELETE
請求)包含有效的 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
是一個 DjangoUser
例項.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 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)
複製程式碼