Django REST框架——教程4:認(rèn)證和權(quán)限

教程4:認(rèn)證和權(quán)限

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

  • 代碼片段始終與創(chuàng)建者相關(guān)聯(lián)。
  • 只有經(jīng)過(guò)身份驗(yàn)證的用戶才可以創(chuàng)建片段。
  • 只有代碼段的創(chuàng)建者可能會(huì)更新或刪除它。
  • 未經(jīng)身份驗(yàn)證的請(qǐng)求應(yīng)具有完全只讀訪問權(quán)限。

將信息添加到我們的模型

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

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

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

我們還需要確保保存模型時(shí),使用pygments代碼高亮庫(kù)來(lái)填充突出顯示的字段。

我們需要一些額外的導(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ù)庫(kù)表。通常我們會(huì)創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)遷移來(lái)完成這個(gè)任務(wù),但是為了本教程的目的,我們只需刪除數(shù)據(jù)庫(kù)并重新啟動(dòng)即可。

rm -f db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate

您可能還想創(chuàng)建幾個(gè)不同的用戶,以用于測(cè)試API。最快的方法就是使用createsuperuser命令。

python manage.py createsuperuser

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

現(xiàn)在我們有一些用戶可以使用,我們最好將這些用戶的表示添加到我們的API中。創(chuàng)建一個(gè)新的序列化器很容易。在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)系,所以在使用這個(gè)ModelSerializer類的時(shí)候默認(rèn)不會(huì)包含它,所以我們需要為它添加一個(gè)顯式的字段。

我們也會(huì)添加幾行代碼到views.py中。我們希望只使用只讀視圖為用戶表示,所以我們將使用ListAPIViewRetrieveAPIView通用的基于類的意見。

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)入這個(gè)UserSerializer

from snippets.serializers import UserSerializer

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

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

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

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

我們處理這個(gè)問題的方式是通過(guò)覆蓋.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'字段,以及來(lái)自請(qǐng)求的驗(yàn)證數(shù)據(jù)。

更新我們的序列化器

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

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

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

這個(gè)字段正在做一些有趣的事情。source參數(shù)控制用于填充的字段,并且可以在對(duì)串行化實(shí)例的任何屬性點(diǎn)。它也可以采用上面顯示的虛線符號(hào),在這種情況下,它將以與Django的模板語(yǔ)言類似的方式遍歷給定的屬性。

我們添加的字段是無(wú)類型的ReadOnlyField類,與其他類型的字段相比,例如CharField,BooleanField等等... untyped ReadOnlyField總是只讀的,并且將被用于序列化的表示,但不會(huì)被用于更新模型當(dāng)它們被反序列化時(shí)。我們也可以CharField(read_only=True)在這里使用。

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

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

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

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

from rest_framework import permissions

接著,下面的屬性添加到SnippetListSnippetDetail視圖類。

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

添加登錄到Browsable API

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

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

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

from django.conf.urls import include

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

urlpatterns += [
    url(r'^api-auth/', include('rest_framework.urls')),
]

r'^api-auth/'模式的一部分實(shí)際上可以是任何您想要使用的URL。

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

一旦創(chuàng)建了一些代碼片段,請(qǐng)導(dǎo)航到“/ users /”端點(diǎn),并注意到代表性包括每個(gè)用戶的“代碼段”字段中與每個(gè)用戶關(guān)聯(lián)的代碼段ID列表。

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

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

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

在snippets應(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)在,我們可以通過(guò)編輯視圖類的permission_classes屬性,將自定義權(quán)限添加到代碼片段實(shí)例端點(diǎn)SnippetDetail

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly,)

確保也要導(dǎo)入這個(gè)IsOwnerOrReadOnly類。

from snippets.permissions import IsOwnerOrReadOnly

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

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

因?yàn)槲覀儸F(xiàn)在在API上擁有一組權(quán)限,所以如果我們要編輯任何代碼片段,我們需要驗(yàn)證我們的請(qǐng)求。我們還沒有設(shè)置任何認(rèn)證類,所以默認(rèn)是當(dāng)前應(yīng)用的,這是SessionAuthenticationBasicAuthentication。

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

如果我們正在以編程方式與API進(jìn)行交互,則需要在每個(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."
}

我們可以通過(guò)包含我們之前創(chuàng)建的用戶的用戶名和密碼來(lái)提出成功的請(qǐng)求。

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

{
    "id": 1,
    "owner": "admin",
    "title": "foo",
    "code": "print 789",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

概要

我們現(xiàn)在已經(jīng)在我們的Web API上獲得了相當(dāng)細(xì)致的權(quán)限集合,以及系統(tǒng)用戶和他們創(chuàng)建的代碼片段的端點(diǎn)。

在本教程的第5部分中,我們將研究如何通過(guò)為突出顯示的片段創(chuàng)建HTML端點(diǎn)來(lái)將所有內(nèi)容綁定在一起,并通過(guò)使用系統(tǒng)內(nèi)關(guān)系的超鏈接來(lái)改進(jì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)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,326評(píng)論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評(píng)論 19 139
  • Zozo_C閱讀 345評(píng)論 3 1
  • 簡(jiǎn)書是一個(gè)很好的閱讀平臺(tái),至少最開始的時(shí)候,我先是被它的簡(jiǎn)約風(fēng)格吸引了過(guò)來(lái),隨后都有習(xí)慣的每天打開看看里面的文章...
    燈塔水母閱讀 276評(píng)論 0 0
  • 置身春山外,心在詩(shī)書前。 流寒水猶淺,帆點(diǎn)扁舟懸。 臘梅伴殘夜,燭光別舊年。 微言何處達(dá),雁飛江之南。
    丁懷超閱讀 327評(píng)論 0 1

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