安全性是任何網站的重要組成部分,但對於 Web API 而言則至關重要。 目前,我們的 Blog API 允許任何人進行完全訪問。 沒有任何限制; 任何用戶都可以做任何極其危險的事情。 例如,匿名用戶可以創建,閱讀,更新或刪除任何博客文章。 他們甚至沒有創造一個! 顯然,我們不希望這樣做。
Django REST Framework 附帶了一些現成的權限設置,我們可以使用這些設置來保護我們的 API。 這些可以應用於項目級別,視圖級別或任何單個模型級別。
在本章中,我們將添加一個新用戶並嘗試多種權限設置。 然後,我們將創建自己的自定義權限,以便只有博客文章的作者才可以更新或刪除它。
首先創建第二個用戶。 這樣,我們可以在兩個用戶帳戶之間切換以測試我們的權限設置。
浏覽至 http://127.0.0.1:8000/admin/ 的管理員。 然後單擊“用戶”旁邊的“ +添加”。
輸入新用戶的用戶名和密碼,然後單擊“保存”按鈕。 我在這裡選擇了用戶名 testuser
。
下一個屏幕是“管理員用戶更改”頁面。 我已經將我的用戶稱為 testuser,在這裡我可以添加默認用戶模型中包含的其他信息,例如名字,姓氏,電子郵件地址等。但是對於我們而言,這些都不是必需的:我們只需要用戶名和密碼用於檢測。
向下滾動到此頁面的底部,然後單擊“保存”按鈕。 它將重定向回位於 http://127.0.0.1:8000/admin/auth/user/
的主用戶頁面。
我們可以看到列表中有兩個用戶。
今後,無論何時要在用戶帳戶之間切換,我們都需要跳到 Django 管理員,退出一個帳戶,然後登錄另一個帳戶。 每次。 然後切換回我們的 API 端點。
這是一種常見的情況,Django REST Framework 具有單行設置以添加登錄並直接注銷到可浏覽的 API 本身。 我們將立即實施。
在項目級別的 urls.py
文件中,添加一個包含 rest_framework.urls
的新 URL 路由。 令人困惑的是,指定的實際路線可以是我們想要的任何東西; 重要的是 rest_framework.urls
包含在某處。 我們將使用 api-auth 路由,因為它與官方文檔匹配,但我們可以輕松使用任何我們想要的東西,並且所有功能都將保持相同。
# blog_project/urls.pyfrom django.contrib import admin from django.urls import include, pathurlpatterns = [ path('admin/', admin.site.urls), path('api/v1/', include('posts.urls')), path('api-auth/', include('rest_framework.urls')), # new]
現在,訪問 http://127.0.0.1:8000/api/v1/
上的可浏覽 API。 有一個細微的變化:右上角的用戶名旁邊是一個向下的箭頭。
由於此時我們已使用超級用戶帳戶登錄(對我而言是 wsv ),因此將顯示該名稱。 單擊鏈接,然後顯示帶有“注銷”的下拉菜單。 點擊它。
右上角的鏈接現在更改為“登錄”。 因此,請點擊該按鈕。
我們被重定向到 Django REST Framework 登錄頁面。 在此處使用您的測試用戶帳戶。
最後,它將重定向到主 API 頁面,在右上角有testuser。
最後,注銷我們的 testuser 帳戶。
您應該再次在右上角看到“Log in”鏈接。
當前,任何匿名非授權用戶都可以訪問我們的 PostList 端點。 之所以知道這一點,是因為即使我們尚未登錄,也可以看到我們的單個博客文章。 更糟糕的是,任何人都有權創建,編輯,更新或刪除帖子!
在詳細信息頁面 http://127.0.0.1:8000/api/v1/1/
上,該信息也是可見的,任何隨機用戶都可以更新或刪除現有博客文章。
之所以仍然可以看到“發布列表”終結點和“詳細列表”終結點,是因為我們之前在settings.py文件中將項目的項目級別權限設置為AllowAny。 簡要提醒一下,它看起來像這樣:
# blog_project/settings.pyREST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.AllowAny', ]}
我們現在想要的是將API訪問限制為經過身份驗證的用戶。 我們可以在多個地方執行此操作-項目級,視圖級或對象級-但是由於目前我們只有兩個視圖,因此我們從那裡開始,並為每個視圖添加權限。
在您的 posts/views.py
文件中,從Django REST框架的頂部導入權限,然後向每個視圖添加一個 permission_classes
字段。
# posts/views.pyfrom rest_framework import generics, permissions # newfrom .models import Postfrom .serializers import PostSerializerclass PostList(generics.ListCreateAPIView): permission_classes = (permissions.IsAuthenticated,) # new queryset = Post.objects.all() serializer_class = PostSerializer class PostDetail(generics.RetrieveUpdateDestroyAPIView): permission_classes = (permissions.IsAuthenticated,) # new queryset = Post.objects.all() serializer_class = PostSerializer
這就是我們所需要的。 通過 http://127.0.0.1:8000/api/v1/
刷新可浏覽的 API。 看看發生了什麼!
我們不再看到我們的“帖子列表”頁面。 取而代之的是,由於我們尚未登錄,因此會收到不友好的 HTTP 403禁止狀態代碼。由於我們沒有權限,因此可浏覽的 API 中沒有表格可以編輯數據。
因此,目前只有登錄的用戶可以查看我們的 API。 如果您使用超級用戶或 testuser
帳戶重新登錄,則可以訪問 API 端點。
但是,請考慮一下隨著 API 復雜性的增長會發生什麼。 將來我們可能會有更多的視圖和終點。 如果我們要在整個 API 中設置相同的權限設置,則向每個視圖添加專用的 permission_class
似乎是重復的。
最好只在項目級別更改一次權限,而不是對每個視圖都進行一次更改,這會更好嗎?
此時,您應該點頭。 在項目級別設置嚴格的權限策略並在視圖級別根據需要放寬策略是一種更簡單,更安全的方法。 這就是我們要做的。
幸運的是,Django REST Framework隨 附了許多我們可以使用的內置項目級權限設置,包括:
AllowAny-任何經過身份驗證的用戶都具有完全訪問權限
IsAuthenticated-只有經過身份驗證的注冊用戶才能訪問
IsAdminUser-只有管理員/超級用戶有權訪問
IsAuthenticatedOrReadOnly-未經授權的用戶可以查看任何頁面,但只能查看經過身份驗證的用戶具有寫,編輯或刪除權限
要實施這四個設置中的任何一個,都需要更 DEFAULT_PERMISSION_CLASSES
設置和刷新我們的 Web 浏覽器。 而已!
讓我們切換到 IsAuthenticated
,這樣只有經過身份驗證或登錄的用戶才能查看API。
更新 blog_project/settings.py
,如下:
# blog_project/settings.pyREST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', # new ]}
現在返回到views.py文件,並刪除我們剛剛進行的權限更改。
# posts/views.pyfrom rest_framework import genericsfrom .models import Postfrom .serializers import PostSerializerclass PostList(generics.ListCreateAPIView): queryset = Post.objects.all() serializer_class = PostSerializer class PostDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Post.objects.all() serializer_class = PostSerializer
如果刷新“發布列表”和“詳細列表” API頁面,您仍將看到相同的403狀態代碼。 現在,我們要求所有用戶都必須先進行身份驗證,然後才能訪問API,但是我們也始終可以根據需要進行其他視圖級更改。
是時候獲得我們的第一個自定義權限了。 作為我們現在的簡要回顧:我們有兩個用戶,testuser和超級用戶帳戶。 我們的數據庫中有一個博客帖子,由超級用戶創建。
我們只希望特定博客文章的作者能夠對其進行編輯或刪除; 否則,博客文章應為只讀。 因此,超級用戶帳戶應具有對單個博客實例的完整CRUD訪問權限,而常規用戶testuser應該沒有。
使用Control + c停止本地服務器,並在我們的帖子應用中創建一個新的 permissions.py
文件。
(blogapi) $ touch posts/permissions.py
在內部,Django REST Framework依賴於BasePermission類,所有其他權限類都從該BasePermission類繼承。 這意味著諸如AllowAny,IsAuthenticated之類的內置權限設置會對其進行擴展。 這是Github上可用的實際源代碼:
class BasePermission(object): """ A base class from which all permission classes should inherit. """ def has_permission(self, request, view): """ Return `True` if permission is granted, `False` otherwise. """ return True def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ return True
要創建自己的自定義權限,我們將覆蓋 has_object_permission
方法。 具體來說,我們希望對所有請求都允許只讀,但對於任何寫入請求(例如編輯或刪除),作者必須與當前登錄的用戶相同。
這是我們的 posts / permissions.py 文件的內容。
# posts/permissions.pyfrom rest_framework import permissionsclass IsAuthorOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): # Read-only permissions are allowed for any request if request.method in permissions.SAFE_METHODS: return True # Write permissions are only allowed to the author of a post return obj.author == request.user
我們在頂部導入權限,然後創建自定義類IsAuthorOrReadOnly,該類擴展了BasePermission。 然後,我們覆蓋has_object_permission。 如果請求包含SAFE_METHODS中包含的HTTP動詞(包含GET,OPTIONS和HEAD的元組),則該請求為只讀請求,並授予權限。
否則,該請求是針對某種類型的寫入的,這意味著需要更新API資源,以便創建,刪除或編輯功能。 在這種情況下,我們檢查所討論對象的作者(即我們的博客文章 obj.author)是否與發出請求request.user的用戶匹配。
回到views.py文件中,我們應該導入 IsAuthorOrReadOnly
,然後我們可以添加PostDetail的``permission_classes`。
# posts/views.pyfrom rest_framework import genericsfrom .models import Postfrom .permissions import IsAuthorOrReadOnly # new from .serializers import PostSerializerclass PostList(generics.ListCreateAPIView): queryset = Post.objects.all() serializer_class = PostSerializerclass PostDetail(generics.RetrieveUpdateDestroyAPIView): permission_classes = (IsAuthorOrReadOnly,) # new queryset = Post.objects.all() serializer_class = PostSerializer
我們完成了。 讓我們測試一下。 導航到“帖子詳細信息”頁面。 確保您使用帖子的作者超級用戶帳戶登錄。 因此它將在頁面的右上角可見。
但是,如果您注銷然後使用 testuser 帳戶登錄,則頁面會更改。
由於允許只讀權限,因此我們可以查看此頁面。 但是,由於自定義的 IsAuthorOrReadOnly 權限類,我們無法發出任何 PUT 或 DELETE 請求。
請注意,通用視圖將僅檢查對象級權限以獲取檢索單個模型實例的視圖。 如果您需要對列表視圖進行對象級過濾(對於實例集合),則需要通過覆蓋初始查詢集進行過濾。
設置適當的權限是任何API的重要組成部分。 作為一般策略,最好設置嚴格的項目級別權限策略,以使只有經過身份驗證的用戶才能查看API。 然後,根據需要在特定的API端點上更易於訪問視圖級別或自定義權限。