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