django restframwork 教程之authentication權限


當前我們的API在編輯或者刪除的時候沒有任何限制,我們不希望有些人有高級的行為,確保:

  • 代碼段始終與創建者相關聯
  • 只允許授權的用戶可以創建代碼段
  • 只允許代碼段創建者可以更新和刪除
  • 沒有認證的請求應該有一個完整的只讀權限列表

添加用戶信息在我們的models中

我們將對snippet models 進行一些更改,首先,讓我們添加一對fields,其中一個跟我們的用戶表關聯,剩下一個字段用作存儲高亮的html,還需要導入pygments庫來高亮code,最后需要重新定義save方法,添加一些我們自定義配置

修改后的models如下

from django.db import models

from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
    owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
    highlighted = models.TextField()

    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)


    class Meta:
        ordering = ('created',)

接下來我們需要將新字段在數據庫中添加,為了中間不必要的麻煩,現在我們直接刪除原來的庫和migration目錄,重新生成

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

最后我們需要創建一個超級用戶來測試API,使用createsuperuser來快速創建

python manage.py createsuperuser

為我們user models添加serializers

我們需要在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')

因為'snippets'是User模型上的反向關系,所以在使用ModelSerializer類時,它不會被默認包含,因此我們需要為它添加一個顯式字段。

我們還會向views.py添加幾個視圖。我們只想對用戶表示使用只讀視圖,因此我們將使用ListAPIView和RetrieveAPIView通用類的視圖。

from django.contrib.auth.models import User
from snippets.serializers import UserSerializer

class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

最后我們需要為剛才的user 視圖添加url配置,修改urls.py

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

將代碼段與用戶關聯

現在,如果我們創建了代碼段,就無法將創建代碼段的用戶與代碼段實例相關聯。用戶不作為序列化表示的一部分發送,而是傳入請求的屬性。
我們處理它的方式是通過在我們的代碼片段視圖上覆蓋一個.perform_create()方法,允許我們修改實例保存的方式,並處理在傳入請求或請求的URL中隱含的任何信息。
在我們SnippetList 視圖類中,添加下面的方法

def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

當我們的serializer里create()方法被調用時,將自動添加owner字段和驗證合法的請求數據

更新我們的seralizer

現在sippets創建的時候已經和我們的用戶關聯起來,讓我們更新我們的SnippetSerializer,添加以下定義的字段:

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

source參數用於控制那個屬性被用來填充字段,並且可以指向序列化實例上的任何屬性。 它也可以采用上面所示的虛線符號,在這種情況下,它將遍歷給定的屬性,與使用Django的模板語言類似的方式。
我們添加的字段是無類型的ReadOnlyField類,與其他類型的字段,例如CharField,BooleanField等相反。無類型的ReadOnlyField總是只讀的,並且將用於序列化表示,但不會 用於在反序列化時更新模型實例。 我們也可以在這里使用CharField(read_only = True)。
同時需要在meta class中添加owner字段

class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style','owner')

在views中添加請求權限

現在snippets 已經和我們的用戶關聯上了,我們需要確保僅僅通過驗證的用戶能夠增刪改

REST framework 包含有一些權限類,我們可以用來限制誰可以訪問給定的視圖,在這種情況下,首先我們查找IsAuthenticatedOrReadOnl,這將確保已驗證的請求獲得讀寫訪問權限,並且未驗證的請求獲得只讀訪問權限。

首先在module中導入permissions,

from rest_framework import permissions

然后在SnippetListSnippetDetail view中添加下面這個屬性

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

添加登陸WEB API

如果您打開瀏覽器並轉到可瀏覽的API,您會發現您無法再創建新的代碼段。為此,我們需要能夠以用戶身份登錄。

我們可以通過編輯項目級urls.py文件中的URLconf來添加一個登錄視圖,以便與可瀏覽的API一起使用
編輯urls.py,添加如下內容

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

r'^api-auth/' 這個可以自定義,但是后面的namespace必須使用rest_framework作命名空間,但是在django1.9+版本,自動設置,不需要添加
現在,如果您再次打開瀏覽器並刷新頁面,您將在頁面右上角看到一個“登錄”鏈接。如果您以之前創建的用戶之一登錄,則可以再次創建代碼段
創建幾個代碼段后,轉到“/ users /”端點,並注意該表示中包含每個用戶的“代碼段”字段中與每個用戶相關聯的代碼段ID列表。

對象級別的權限

我們希望所有的代碼片段對任何人都可見,但也確保只有創建代碼片段的用戶能夠更新或刪除它。為了實現這個,我們需要創建一個自定義權限
在snippets.app中,創建一個新的文件,名為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

現在我們在SnippetDetail view中通過編輯permission_classes屬性為snippet實例中添加自定義的權限

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly,)

同時確保導入了自定的權限類

from snippets.permissions import IsOwnerOrReadOnly

現在,如果您再次打開瀏覽器,則會發現如果您以創建代碼段的同一用戶身份登錄,則“DELETE”和“PUT”操作只會顯示在代碼段實例端點上。

使用api進行身份驗證

因為我們現在有一組API的權限,如果我們想要編輯任何snippet,我們需要使用SessionAuthenticationBasicAuthentication驗證我們的請求
當我們通過Web瀏覽器與API交互時,我們可以登錄,然后瀏覽器會話將為請求提供所需的身份驗證。
如果我們以編程方式與API交互,我們需要在每個請求中顯式提供身份驗證憑據。
如果我們嘗試創建一個未驗證的代碼段,我們會收到一條錯誤:

{
    "detail": "Authentication credentials were not provided."
}

我們在請求的時候加入驗證,我們將可以創建:

curl  -X POST  -s -u test:1234.com -H "Content-Type:application/json"  -d  '{"title": "sec test","code": "python asdasdas manasdnasd", "linenos": false,"language": "python","style": "vim","owner": "fuzengjie"}' http://127.0.0.1:8000/snippets/ | jq .
{
  "id": 2,
  "title": "sec test",
  "code": "python asdasdas manasdnasd",
  "linenos": false,
  "language": "python",
  "style": "vim",
  "owner": "test"
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM