[Django REST FrameWork][4]:驗(yàn)證和權(quán)限

目前,我們的API對(duì)誰可以編輯或刪除代碼段沒有任何限制。我們想要一些更高級(jí)的行為,以確保:

代碼段始終與創(chuàng)建者相關(guān)聯(lián)。

只有身份驗(yàn)證的用戶可以創(chuàng)建片段。

只有片段的創(chuàng)建者可以更新或刪除它。

未經(jīng)身份驗(yàn)證的請(qǐng)求應(yīng)具有完全只讀訪問權(quán)限。

將信息添加到我們的模型

我們將對(duì)我們的Snippet模型類進(jìn)行一些更改。首先,我們添加幾個(gè)字段。其中一個(gè)字段將用于表示創(chuàng)建代碼段的用戶。另一個(gè)字段將用于存儲(chǔ)代碼的突出顯示的HTML表示。

將以下兩個(gè)字段添加到Snippet模型中models.py。

owner?=?models.ForeignKey('auth.User',?related_name='snippets',?on_delete=models.CASCADE)

highlighted?=?models.TextField()

我們還需要確保在保存模型時(shí),使用pygments代碼突出顯示庫填充突出顯示的字段。

我們需要一些額外的導(dǎo)入:

from?pygments.lexers?import?get_lexer_by_name

from?pygments.formatters.html?import?HtmlFormatter

from?pygments?import?highlight

現(xiàn)在我們可以.save()在我們的模型類中添加一個(gè)方法:

def?save(self,?*args,?**kwargs):

????"""

????Use?the?`pygments`?library?to?create?a?highlighted?HTML

????representation?of?the?code?snippet.

????"""

????lexer?=?get_lexer_by_name(self.language)

????linenos?=?self.linenos?and?'table'?or?False

????options?=?self.title?and?{'title':?self.title}?or?{}

????formatter?=?HtmlFormatter(style=self.style,?linenos=linenos,

??????????????????????????????full=True,?**options)

????self.highlighted?=?highlight(self.code,?lexer,?formatter)

????super(Snippet,?self).save(*args,?**kwargs)

完成這些工作后,我們需要更新我們的數(shù)據(jù)庫表。通常我們將創(chuàng)建一個(gè)數(shù)據(jù)庫遷移,為了做到這一點(diǎn),但是為了本教程的目的,我們只需刪除數(shù)據(jù)庫并重新開始

rm?-f?tmp.db?db.sqlite3

rm?-r?snippets/migrations

python?manage.py?makemigrations?snippets

python?manage.py?migrate

您可能還需要?jiǎng)?chuàng)建幾個(gè)不同的用戶,以用于測(cè)試API。執(zhí)行此操作的最快方法是使用createsuperuser命令。

python?manage.py?createsuperuser

為我們的用戶模型添加端點(diǎn)

現(xiàn)在我們有一些用戶可以使用,我們最好將這些用戶的表示添加到我們的API中。創(chuàng)建一個(gè)新的serializer很容易。在serializers.py添加:

from?django.contrib.auth.models?import?User

class?UserSerializer(serializers.ModelSerializer):

????snippets?=?serializers.PrimaryKeyRelatedField(many=True,?queryset=Snippet.objects.all())

????class?Meta:

????????model?=?User

????????fields?=?('id',?'username',?'snippets')

因?yàn)樵赨ser模型上'snippets'是一個(gè)反向關(guān)系,所以在使用ModelSerializer該類時(shí)它不會(huì)被默認(rèn)包含,所以我們需要為它添加一個(gè)顯式字段。

我們還會(huì)添加幾個(gè)視圖views.py。我們希望只使用只讀視圖為用戶表示,所以我們將使用ListAPIView和RetrieveAPIView通用的基于類的意見。

from?django.contrib.auth.models?import?User

class?UserList(generics.ListAPIView):

????queryset?=?User.objects.all()

????serializer_class?=?UserSerializer

class?UserDetail(generics.RetrieveAPIView):

????queryset?=?User.objects.all()

????serializer_class?=?UserSerializer

確保也導(dǎo)入U(xiǎn)serSerializer類

from?snippets.serializers?import?UserSerializer

最后,我們需要通過從URL conf引用它們將這些視圖添加到API中。將以下內(nèi)容添加到其中的模式中urls.py。

url(r'^users/$',?views.UserList.as_view()),

url(r'^users/(?P[0-9]+)/$',?views.UserDetail.as_view()),

將片段與用戶關(guān)聯(lián)

現(xiàn)在,如果我們創(chuàng)建了一個(gè)代碼片段,那么將無法將創(chuàng)建該代碼段的用戶與代碼段實(shí)例相關(guān)聯(lián)。用戶不是作為序列化表示的一部分發(fā)送的,而是傳入請(qǐng)求的屬性。

我們處理的方式是覆蓋.perform_create()我們的代碼片段視圖上的方法,這樣我們可以修改實(shí)例保存的管理方式,并處理傳入請(qǐng)求或請(qǐng)求的URL中隱含的任何信息。

在SnippetList視圖類中,添加以下方法:

def?perform_create(self,?serializer):

????serializer.save(owner=self.request.user)

create()我們的串行器的方法現(xiàn)在將被傳遞一個(gè)附加'owner'字段,以及請(qǐng)求中驗(yàn)證的數(shù)據(jù)。

更新我們的serializer

現(xiàn)在,這些片段與創(chuàng)建它們的用戶相關(guān)聯(lián),我們更新我們SnippetSerializer來反映這一點(diǎn)。將以下字段添加到序列化器定義中serializers.py:

owner?=?serializers.ReadOnlyField(source='owner.username')

注意:確保您還添加'owner',到內(nèi)部Meta類的字段列表。

這個(gè)領(lǐng)域正在做一些很有趣的事情。的source哪個(gè)屬性參數(shù)控制用于填充的字段,并且可以在對(duì)串行化實(shí)例的任何屬性點(diǎn)。它也可以采用上面顯示的點(diǎn)劃線,在這種情況下,它將以與Django模板語言一起使用的相似方式遍歷給定的屬性。

我們添加了字段是類型化ReadOnlyField類,相對(duì)于其他類型的字段,如CharField,BooleanField等...類型化ReadOnlyField始終是只讀的,并且將用于序列化表示形式,但不會(huì)被用于更新模型他們被反序列化的實(shí)例。我們也可以CharField(read_only=True)在這里使用。

添加視圖所需的權(quán)限

現(xiàn)在,代碼片段與用戶相關(guān)聯(lián),我們希望確保只有經(jīng)過身份驗(yàn)證的用戶才能創(chuàng)建,更新和刪除代碼段。

REST框架包括許多權(quán)限類,我們可以使用它們來限制誰可以訪問給定的視圖。在這種情況下,我們正在尋找的是IsAuthenticatedOrReadOnly,這將確保經(jīng)過身份驗(yàn)證的請(qǐng)求獲得讀寫訪問權(quán)限,未經(jīng)身份驗(yàn)證的請(qǐng)求將獲得只讀訪問權(quán)限。

首先在視圖模塊中添加以下導(dǎo)入

from?rest_framework?import?permissions

接著,下面的屬性添加到在SnippetList和SnippetDetail視圖類。

permission_classes?=?(permissions.IsAuthenticatedOrReadOnly,)

添加登錄到Browsable API

如果您打開瀏覽器并導(dǎo)航到目前可瀏覽的API,那么您將發(fā)現(xiàn)無法再創(chuàng)建新的代碼段。為了做到這一點(diǎn),我們需要能夠以用戶身份登錄。

我們可以通過編輯項(xiàng)目級(jí)urls.py文件中的URLconf來添加可瀏覽API使用的登錄視圖。

在文件頂部添加以下導(dǎo)入:

from?django.conf.urls?import?include

并且,在文件末尾,添加一個(gè)模式以包括可瀏覽的API的登錄和注銷視圖。

urlpatterns?+=?[

????url(r'^api-auth/',?include('rest_framework.urls',

???????????????????????????????namespace='rest_framework')),

]

r'^api-auth/'模式的一部分實(shí)際上可以是您要使用的任何URL。唯一的限制是所包含的URL必須使用'rest_framework'命名空間。在Django 1.9+中,REST框架將設(shè)置命名空間,因此您可以將其刪除。

現(xiàn)在,如果再次打開瀏覽器并刷新頁面,您將在頁面右上角看到一個(gè)“登錄”鏈接。如果您以您之前創(chuàng)建的用戶身份登錄,則可以再次創(chuàng)建代碼段。

創(chuàng)建幾個(gè)代碼片段后,導(dǎo)航到“/ users /”端點(diǎn),并注意到該表示包含每個(gè)用戶的“片段”字段中與每個(gè)用戶相關(guān)聯(lián)的代碼段的列表。

對(duì)象級(jí)權(quán)限

我們希望所有的代碼片段都可以被任何人看到,但也要確保只有創(chuàng)建代碼段的用戶才能更新或刪除它。

要做到這一點(diǎn),我們將需要?jiǎng)?chuàng)建一個(gè)自定義權(quán)限。

在片段應(yīng)用中,創(chuàng)建一個(gè)新文件,permissions.py

from?rest_framework?import?permissions

class?IsOwnerOrReadOnly(permissions.BasePermission):

????"""

????Custom?permission?to?only?allow?owners?of?an?object?to?edit?it.

????"""

????def?has_object_permission(self,?request,?view,?obj):

????????#?Read?permissions?are?allowed?to?any?request,

????????#?so?we'll?always?allow?GET,?HEAD?or?OPTIONS?requests.

????????if?request.method?in?permissions.SAFE_METHODS:

????????????return?True

????????#?Write?permissions?are?only?allowed?to?the?owner?of?the?snippet.

????????return?obj.owner?==?request.user

現(xiàn)在,我們可以通過編輯視圖類中的permission_classes屬性將該自定義權(quán)限添加到我們的代碼段實(shí)例端點(diǎn)SnippetDetail:

permission_classes?=?(permissions.IsAuthenticatedOrReadOnly,

??????????????????????IsOwnerOrReadOnly,)

確保也導(dǎo)入IsOwnerOrReadOnly類。

from?snippets.permissions?import?IsOwnerOrReadOnly

現(xiàn)在,如果再次打開瀏覽器,您會(huì)發(fā)現(xiàn)如果您以與創(chuàng)建代碼段相同的用戶身份登錄,“DELETE”和“PUT”操作只會(huì)顯示在代碼段實(shí)例端點(diǎn)上。

使用API進(jìn)行身份驗(yàn)證

因?yàn)槲覀儸F(xiàn)在有一組API的權(quán)限,如果我們要編輯任何片段,我們需要驗(yàn)證我們的請(qǐng)求。我們還沒有設(shè)置任何身份驗(yàn)證類,所以默認(rèn)值現(xiàn)在被應(yīng)用,哪些是SessionAuthentication和BasicAuthentication。

當(dāng)我們通過Web瀏覽器與API進(jìn)行交互時(shí),我們可以登錄,然后瀏覽器會(huì)話將為請(qǐng)求提供所需的身份驗(yàn)證。

如果我們以編程方式與API交互,我們需要在每個(gè)請(qǐng)求上顯式提供身份驗(yàn)證憑據(jù)。

如果我們嘗試創(chuàng)建一個(gè)沒有驗(yàn)證的代碼段,我們會(huì)收到一個(gè)錯(cuò)誤:

http?POST?http://127.0.0.1:8000/snippets/?code="print?123"

{

????"detail":?"Authentication?credentials?were?not?provided."

}

我們可以通過包括我們之前創(chuàng)建的一個(gè)用戶的用戶名和密碼來成功提出請(qǐng)求。

http?-a?tom:password123?POST?http://127.0.0.1:8000/snippets/?code="print?789"

{

????"id":?1,

????"owner":?"tom",

????"title":?"foo",

????"code":?"print?789",

????"linenos":?false,

????"language":?"python",

????"style":?"friendly"

}

概要

我們現(xiàn)在已經(jīng)在我們的Web API上獲得了一個(gè)相當(dāng)精細(xì)的權(quán)限,并為系統(tǒng)的用戶和他們創(chuàng)建的代碼段提供了終點(diǎn)。

在本教程的第5部分中,我們將介紹如何通過為突出顯示的片段創(chuàng)建一個(gè)HTML端點(diǎn)來將所有內(nèi)容聯(lián)結(jié)在一起,并通過為系統(tǒng)中的關(guān)系使用超鏈接來提高API的凝聚力。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,232評(píng)論 3 119
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,282評(píng)論 8 265
  • 教程4:認(rèn)證和權(quán)限 目前我們的API對(duì)誰可以編輯或刪除代碼片段沒有任何限制。我們希望有一些更高級(jí)的行為,以確保: ...
    loveroot閱讀 363評(píng)論 0 0
  • 一個(gè)具有優(yōu)秀領(lǐng)導(dǎo)潛質(zhì)的人物,不一定需要事必躬親,不一定時(shí)刻表露出高高在上的權(quán)威,他只需要成為居中最具粘合作用的支點(diǎn)...
    李艷哲閱讀 291評(píng)論 0 1
  • 風(fēng)走了,云追隨; 光走了,亮追隨; 花走了,香追隨; 你走了,我追隨。 你是陽光,溫暖我冰冷的軀體; 你是導(dǎo)航,指...
    水木聆風(fēng)_杰閱讀 341評(píng)論 4 1

友情鏈接更多精彩內(nèi)容