身份驗證功能需要可插拔?!?Jacob Kaplan-Moss, "REST worst practices"
身份驗證是將傳入請求與一組標識憑據(jù)(例如請求來自的用戶或其簽名的令牌)相關(guān)聯(lián)的機制。然后,權(quán)限 和 限制 可以使用這些憑據(jù)來確定是否應(yīng)允許該請求。
REST framework 提供了一些開箱即用的身份驗證方案,并且還允許你實現(xiàn)自定義方案。
驗證始終在視圖的最開始進行,在執(zhí)行權(quán)限和限制檢查之前以及允許任何其他代碼繼續(xù)執(zhí)行之前。
request.user 屬性通常被設(shè)置為contrib.auth 包中 User 類的一個實例。
request.auth 屬性用于任何其他身份驗證信息,例如,它可以用于表示請求簽名的身份驗證令牌。
注意: 不要忘了認證本身不會允許或拒絕傳入的請求,它只是簡單識別請求攜帶的憑證。
認證方案總是被定義為一個類的列表。REST framework 將嘗試使用列表中的每個類進行身份驗證,并使用成功完成驗證的第一個類的返回值設(shè)置 request.user 和request.auth。
如果沒有類進行驗證,request.user 將被設(shè)置成 django.contrib.auth.models.AnonymousUser的實例,request.auth 將被設(shè)置成None。
未認證請求的request.user 和 request.auth 的值可以使用 UNAUTHENTICATED_USER和UNAUTHENTICATED_TOKEN 設(shè)置進行修改。
可以使用 DEFAULT_AUTHENTICATION_CLASSES 設(shè)置全局的默認身份驗證方案。比如:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
你還可以使用基于APIView類視圖的方式,在每個view或每個viewset基礎(chǔ)上設(shè)置身份驗證方案。
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` 實例。
'auth': unicode(request.auth), # None
}
return Response(content)
或者,如果你使用基于函數(shù)的視圖,那就使用@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` 實例。
'auth': unicode(request.auth), # None
}
return Response(content)
當未經(jīng)身份驗證的請求被拒絕時,有下面兩種不同的錯誤代碼可使用。
HTTP 401 響應(yīng)必須始終包括一個WWW-Authenticate頭,指示客戶端如何進行身份驗證。 HTTP 403響應(yīng)不包括WWW-Authenticate。
具體使用哪種響應(yīng)取決于認證方案。雖然可以使用多種認證方案,但是僅可以使用一種方案來確定響應(yīng)的類型。在確定響應(yīng)類型時,將使用視圖上設(shè)置的第一個認證類。
注意,當一個請求通過了驗證但是被拒絕執(zhí)行請求的權(quán)限時,不管認證方案是什么,都要使用 403 Permission Denied 響應(yīng)。
注意,如果使用 Apache using mod_wsgi部署,認證頭默認不會傳遞給WSGI應(yīng)用程序,它假定由Apache處理認證,而不是在應(yīng)用層面處理。
如果你正在部署到Apache,并且使用任何non-session的身份驗證,則需要顯式配置mod_wsgi才能將所需的頭文件傳遞給應(yīng)用程序。這可以通過在適當?shù)纳舷挛闹兄付╓SGIPassAuthorization指令并將其設(shè)置為'On'來完成。
# 這可能會在服務(wù)器配置,虛擬主機,目錄或.htaccess
WSGIPassAuthorization On
此認證方案使用HTTP 基本認證,針對用戶的用戶名和密碼進行認證?;菊J證通常只適用于測試。
如果認證成功 BasicAuthentication 提供以下信息。
那些被拒絕的未經(jīng)身份驗證的請求會返回使用適當WWW-Authenticate標頭的HTTP 401 Unauthorized響應(yīng)。例如:
WWW-Authenticate: Basic realm="api"
注意: 如果你在生產(chǎn)中使用BasicAuthentication,那么你必須確保你的API僅在https中可用。你還應(yīng)確保你的API客戶端始終在登錄時重新請求用戶名和密碼,并且不會將這些詳細信息存儲到持久存儲中。
該認證方案使用簡單的基于Token的HTTP認證方案。Token認證適用于客戶端 - 服務(wù)器設(shè)置,如本地桌面和移動客戶端。
要使用TokenAuthentication方案,你需要配置認證類 以便包含TokenAuthentication,另外在INSTALLED_APPS設(shè)置中還需要包含rest_framework.authtoken:
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
注意: 確保在修改設(shè)置后運行一下manage.py migrate。rest_framework.authtoken app 會提交一些Django數(shù)據(jù)庫遷移操作。
你還需要為你的用戶創(chuàng)建令牌。
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=...)
print token.key
對客戶端進行身份驗證,token需要包含在 AuthorizationHTTP頭中。密鑰應(yīng)該是以字符串"Token"為前綴,以空格分割的兩個字符串。例如:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
注意: 如果你想在HTTP頭中使用其他的關(guān)鍵字,比如Bearer,只需要繼承TokenAuthentication類并設(shè)置 keyword類變量。
如果認證成功,TokenAuthentication 提供以下認證信息:
那些被拒絕的未經(jīng)身份驗證的請求會返回使用適當WWW-Authenticate標頭的HTTP 401 Unauthorized響應(yīng)。例如:
WWW-Authenticate: Token
命令行工具curl 可用于測試基于Token認證的API,例如:
curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'
注意: 如果你在生產(chǎn)環(huán)境下使用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在啟動時導入的其他位置。
如果你已經(jīng)創(chuàng)建了一些用戶,則可以如下所示為所有現(xiàn)有用戶生成令牌:
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)
當使用TokenAuthentication時,你可能希望為客戶端提供一個獲取給定用戶名和密碼的令牌的機制。 REST framework 提供了一個內(nèi)置的視圖來提供這個功能。要使用它,需要將 obtain_auth_token 視圖添加到你的URLconf:
from rest_framework.authtoken import views
urlpatterns += [
url(r'^api-token-auth/', views.obtain_auth_token)
]
注意URL正則匹配模式那里可以是你想要使用的任何內(nèi)容。
當使用form表單或JSON將有效的username和password字段POST提交到視圖時,obtain_auth_token視圖將返回JSON響應(yīng):
{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
請注意,默認的obtain_auth_token視圖顯式使用JSON請求和響應(yīng),而不是使用settings中配置的默認渲染器和解析器類。如果需要自定義版本的obtain_auth_token視圖,可以通過重寫ObtainAuthToken類,并在url conf中使用它來實現(xiàn)。
默認情況下,沒有權(quán)限或限制應(yīng)用于obtain_auth_token視圖。如果你希望應(yīng)用限制,則需要重寫視圖類,并使用throttle_classes屬性包含它們。
也可以通過管理界面手動創(chuàng)建令牌。如果你使用的是大型用戶群,我們建議你動態(tài)修改TokenAdmin類,以根據(jù)你的需要進行自定義,更具體地說,將user字段聲明為raw_field。
your_app/admin.py:
from rest_framework.authtoken.admin import TokenAdmin
TokenAdmin.raw_id_fields = ('user',)
此認證方案使用Django的默認session后端進行身份驗證。Session身份驗證適用于與你的網(wǎng)站在相同的Session環(huán)境中運行的AJAX客戶端。
如果成功驗證,SessionAuthentication 提供以下憑據(jù)。
那些被拒絕的未經(jīng)身份驗證的請求會返回HTTP 403 Forbidden響應(yīng)。
如果你正在使用帶有SessionAuthentication的AJAX樣式的API,你需要確保任何"任何"不安全的HTTP方法調(diào)用(如:PUT, PATCH, POST or DELETE請求)都包含有效的CSRF token。有關(guān)詳細信息,請參閱Django CSRF 文檔。
警告:在創(chuàng)建登陸頁面時,始終要使用Django的標準登陸視圖。這樣才能確保你的登陸視圖被正確的認證保護。
由于需要同時支持session和non-session非會話身份驗證,REST框架中的CSRF驗證與標準Django中的工作方式略有不同。這意味著只有經(jīng)過身份驗證的請求需要CSRF令牌,匿名請求可能不會發(fā)送CSRF令牌tokens。此行為不適用于始終需要使用CSRF驗證的登錄視圖。
要實現(xiàn)自定義的認證方案,要繼承BaseAuthentication類并且重寫.authenticate(self, request) 方法。如果認證成功,該方法應(yīng)返回(user, auth)的二元元組,否則返回None。
在某些情況下,你可能不想返回None,而是希望從.authenticate()方法拋出AuthenticationFailed異常。
通常你應(yīng)該采取的方法是:
你也可以重寫.authenticate_header(self, request)方法。如果實現(xiàn)該方法,則應(yīng)返回一個字符串,該字符串將用作HTTP 401 Unauthorized響應(yīng)中的WWW-Authenticate頭的值。
如果.authenticate_header()方法未被重寫,則認證方案將在未驗證的請求被拒絕訪問時返回HTTP 403 Forbidden響應(yīng)。
以下示例將以自定義請求標頭中名稱為'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)
以下第三方包都是可用的。
Django OAuth Toolkit 包提供了OAuth 2.0 認證支持,并且兼容Python 2.7和Python 3.3+。這個包使用優(yōu)秀的OAuthLib,由Evonove維護。該軟件包有很完善的文檔,并得到很好的支持,目前是我們推薦使用的OAuth 2.0支持軟件包。
使用pip安裝。
pip install django-oauth-toolkit
把這個包添加到你的INSTALLED_APPS中,并且修改你的REST framework設(shè)置。
INSTALLED_APPS = (
...
'oauth2_provider',
)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.ext.rest_framework.OAuth2Authentication',
)
}
更多詳情請參閱Django REST framework - Getting started文檔。
Django REST framework OAuth包提供OAuth1和OAuth2支持。
這個軟件包以前直接包含在REST framework中,但現(xiàn)在已被作為第三方軟件包支持和維護。
使用pip進行安裝。
pip install djangorestframework-oauth
更多配置和使用信息請查閱Django REST framework OAuth文檔中的authentication和permissions。
HTTP摘要認證是一種廣泛實現(xiàn)的方案,旨在替代HTTP基本認證,并提供簡單的加密認證機制。Juan Riaza維護著djangorestframework-digestauth為REST framework提供了HTTP摘要認證支持。
Rediker Software的Django OAuth2 Consumer是另一個為REST框架提供OAuth 2.0 support for REST framework的軟件包。該包包含tokens范圍限制權(quán)限,允許對你的API進行更細粒度的訪問。
JSON Web Token是一種相當新的標準,可用于基于token的身份驗證。與內(nèi)置的TokenAuthentication方案不同,JWT身份驗證不需要使用數(shù)據(jù)庫來驗證令牌。Blimp維護djangorestframework-jwt軟件包,它提供了一個JWT Authentication類以及一個機制,客戶端獲得一個給定用戶名和密碼的JWT。
HawkREST庫基于Mohawk庫,讓你可以在API中使用Hawk簽名的請求和響應(yīng)。Hawk讓雙方使用共享密鑰簽名的消息彼此安全地進行通信。它基于HTTP MAC access authentication訪問認證(它基于OAuth 1.0的部分)。
HTTP簽名(目前為IETF草案)提供了一種實現(xiàn)HTTP消息的源認證和消息完整性的方法。與Amazon的HTTP簽名方案類似,許多服務(wù)使用它,它允許無狀態(tài)的每個請求的身份驗證。Elvio Toccalino維護了djangorestframework-httpsignature包,提供了一個易于使用的HTTP簽名身份驗證機制。
Djoser庫提供一組視圖來處理基本操作,如注冊,登錄,注銷,密碼重置和帳戶激活。該包使用自定義用戶模型,它使用基于token的身份驗證。這是一個可以使用REST實現(xiàn)的Django認證系統(tǒng)。
Django-rest-auth庫提供了一組REST API端點,用于注冊,身份驗證(包括社交媒體身份驗證),密碼重置,檢索和更新用戶詳細信息等。有了這些API端點之后,你的客戶端應(yīng)用程序(如AngularJS,iOS,Android和其他)可以通過REST API獨立通信到Django后端站點,以進行用戶管理。
Django-rest-framework-social-oauth2庫提供了一種將社交插件(facebook,twitter,google等)集成到你的身份驗證系統(tǒng)和簡單的oauth2設(shè)置的簡單方法。使用這個庫,你將能夠根據(jù)外部token(例如,F(xiàn)acebook訪問token)對用戶進行身份驗證,將這些令牌轉(zhuǎn)換為“內(nèi)部”oauth2 tokens,并使用和生成oauth2 tokens來驗證用戶。
Django-rest-knox庫提供了模型和視圖,以比內(nèi)置的TokenAuthentication方案更安全和可擴展的方式來處理基于token的身份驗證 - 使用單頁面應(yīng)用程序和移動客戶端能夠一起。它為每個客戶端提供tokens,以及在提供一些其他身份驗證(通常是基本身份驗證)時生成tokens,刪除token(提供服務(wù)器強制注銷)和刪除所有tokens(注銷用戶登錄的所有客戶端)的視圖。
更多建議: