В этом руководстве вы изучите разрешения Django REST Framework и узнаете, как определить пользовательский класс разрешений для проекта списка задач в Python.
Что такое разрешения Django REST Framework
Django REST Framework имеет некоторые встроенные настройки разрешений, которые можно использовать для защиты API. Django REST Framework позволяет устанавливать разрешения на трех уровнях:
- Project-level
- View-level
- Model-level
Разрешение Project-level
Разрешения на уровне проекта задаются в единственной настройке Django под названием REST_FRAMEWORK в файле settings.py проекта Django.
По умолчанию Django REST Framework разрешает неограниченный доступ к API. Это эквивалентно следующему:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
]
}
Помимо классов разрешений AllowAny, Django REST Framework также предлагает другие встроенные разрешения на уровне проекта:
- IsAuthenticated: доступ имеют только аутентифицированные пользователи.
- IsAdminUser: доступ имеют только администраторы/суперпользователи.
- IsAuthenticatedOrReadOnly: неаутентифицированные пользователи могут вызывать любой API, но только аутентифицированные пользователи могут создавать, обновлять и удалять привилегии.
Давайте воспользуемся классом разрешений IsAuthenticated:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
Если вы выйдете из системы как суперпользователь и получите доступ к конечной точке API, которая показывает все задачи /api/v1/todos/, вы получите следующее сообщение об ошибке HTTP 403 «Запрещено»:
HTTP 403 Forbidden
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"detail": "Authentication credentials were not provided."
}
Причина в том, что API требует аутентификации пользователя.
Давайте создадим обычного пользователя с именем testapi, используя сайт администратора http://localhost:8000/admin/1. Обратите внимание, что обычный пользователь не является суперпользователем.
Чтобы разрешить обычному пользователю testapi входить и выходить из системы, необходимо обновить URLconf на уровне проекта, чтобы создать представления входа/выхода:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('api.urls')),
path("auth/", include("rest_framework.urls")), # added
]
После установки rest_framework.urls вы можете получить доступ к конечной точке API /api/v1/todos/. Небольшое изменение — ссылка для входа на правой стороне:

Если вы нажмете ссылку «Войти», вы увидите следующую форму входа:

Войдите в API с возможностью просмотра, используя обычного пользователя testapi и пароль. После успешного входа с пользователем testapi вы увидите следующий ответ:

Разрешения View-level
Django REST Framework позволяет добавлять разрешения на уровне представления для более детального контроля.
Например, вы можете обновить представление TodoDetail так, чтобы его могли просматривать только администраторы, а обычные пользователи не имели к нему доступа.
class TodoDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes =(permissions.IsAdminUser,) # added
queryset = Todo.objects.all()
serializer_class = TodoSerializer
Представление TodoDetail имеет permission_classes, который инициализируется как permissions.IsAdminUser.
Благодаря этому Django REST Framework позволяет только администратору получать доступ к представлению TodoDetail, которое сопоставляется с конечной точкой API /api/v1/todos/id.
Если вы используете пользователя testapi (обычного пользователя) для доступа к API Todo Detail, вы получите ошибку HTTP 403 «Запрещено»:

Чтобы выйти из системы пользователя testapi, щелкните ссылку с именем пользователя и выберите ссылку «Нажмите кнопку выхода»:

И войдите в API с возможностью просмотра, используя супер-администратора (Джон).
Поскольку представление TodoDetail позволяет суперадминистратору получить доступ, вы можете увидеть ответ API:

Пользовательские разрешения
В проекте Todo вы можете ограничить доступ так, что только владельцы todo могут просматривать, редактировать, обновлять и удалять. Но они не могут получить доступ к todo других пользователей. Кроме того, суперпользователь будет иметь доступ к своему собственному списку todo, а также управлять todo других пользователей.
Чтобы удовлетворить эти требования к разрешениям, вам необходимо использовать специальные разрешения Django REST Framework.
Чтобы определить пользовательское разрешение, вы используете класс BasePermission Django REST Framework. Вот исходный код класса BasePermission:
class BasePermission(metaclass=BasePermissionMetaclass):
"""
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
BasePermission имеет два метода:
- has_permission(self, request, view) – Возвращает True, если разрешение предоставлено, или False в противном случае. Представления списка вызовут метод has_permission для проверки разрешений.
- has_object_permission(self, requests, view, obj) – возвращает True, если разрешение предоставлено, или False в противном случае. Метод подробного представления сначала вызовет has_permission(). Если все проходит, он вызовет метод has_object_permission(), чтобы проверить разрешения.
Хорошей практикой является явное переопределение обоих методов, чтобы избежать нежелательных настроек по умолчанию базового класса.
Ниже показаны шаги по определению и использованию пользовательского класса разрешений в Django REST Framework:
- Сначала определите класс IsOwnerOnly, который расширяет класс BasePermission в файле permissions.py:
from rest_framework import permissions
class IsOwnerOnly(permissions.BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
if request.user.is_superuser:
return True
return obj.user == request.user
has_permission всегда возвращает True. Это означает, что любой пользователь может получить доступ к представлению TodoList, чтобы получить все задачи и создать новую задачу. Обратите внимание, что для ограничения доступа к задачам текущего пользователя мы сделаем это позже в представлении TodoList.
Метод has_object_permission возвращает True, если текущий пользователь является суперпользователем или владельцем задачи.
- Во-вторых, обновите классы представлений в файле views.py приложения API, чтобы использовать пользовательский класс разрешений IsOwnerOnly:
from rest_framework import generics
from .serializers import TodoSerializer
from .permissions import IsOwnerOnly
from todo.models import Todo
class TodoList(generics.ListCreateAPIView):
permission_classes =(IsOwnerOnly,) # added
queryset = Todo.objects.all()
serializer_class = TodoSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
# added
def filter_queryset(self, queryset):
queryset = queryset.filter(user=self.request.user)
return super().filter_queryset(queryset)
class TodoDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes =(IsOwnerOnly,) # added
queryset = Todo.objects.all()
serializer_class = TodoSerializer
Оба класса TodoList и TodoDetail используют класс IsOwnerOnly:
permission_classes =(IsOwnerOnly,) # added
Также добавьте метод filter_queryset в класс TodoList, чтобы возвращать только задачи текущего аутентифицированного пользователя:
def filter_queryset(self, queryset):
queryset = queryset.filter(user=self.request.user)
return super().filter_queryset(queryset)
Если вы войдете в API с возможностью просмотра, используя учетную запись суперпользователя, вы сможете просматривать, создавать, редактировать, обновлять и удалять задачи. Но если вы войдете в систему, используя обычного пользователя(testapi), вы увидите, что /api/v1/todos/ возвращает пустой список.
Кроме того, доступ к конечной точке API /api/v1/todos/1/ приведет к ошибке HTTP 403 «Запрещено», поскольку у пользователя testapi нет доступа к задаче, которая не принадлежит этому пользователю:

Теперь вы можете использовать пользователя testapi для создания новой задачи со следующей информацией:
{
"title": "Test API",
"completed": false
}
Вот ответ:
HTTP 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"id": 5,
"title": "Test API",
"completed": false,
"user": "testapi"
}
Сделайте HTTP-запрос GET к конечной точке /api/v1/todos/, и вы получите список задач, созданный пользователем testapi:
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": 5,
"title": "Test API",
"completed": false,
"user": "testapi"
}
]
Также вы можете протестировать просмотр, обновление и удаление todo с идентификатором 5, используя пользователя testapi. Это должно работать так, как и ожидалось.
