Права доступа и токены в DRF
Аутентификация и права в API
В API, как и на сайте, нужно знать, кто делает запрос (аутентификация) и что ему можно (права). DRF разделяет эти задачи:
- классы аутентификации определяют
request.user; - классы прав решают, пустить ли запрос.
Классы прав
| Класс | Кого пускает |
|---|---|
AllowAny |
Всех |
IsAuthenticated |
Только аутентифицированных |
IsAuthenticatedOrReadOnly |
Читать — всех, менять — аутентифицированных |
IsAdminUser |
Только is_staff |
DjangoModelPermissions |
По правам модели (add/change/delete) |
Глобально:
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticatedOrReadOnly"],
}
Для класса:
class PostViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
Безопасные методы — GET, HEAD, OPTIONS (permissions.SAFE_METHODS): они ничего не меняют.
Своё право: только автор редактирует
# blog/api/permissions.py
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
message = "Редактировать пост может только его автор."
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
has_permission(request, view)— проверка на уровне запроса (до получения объекта);has_object_permission(request, view, obj)— проверка конкретного объекта (в detail-запросах).
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsAuthorOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
Все права из списка должны разрешить запрос.
Способы аутентификации
SessionAuthentication
Использует сессию Django — удобно для Browsable API и фронтенда на том же домене (нужен CSRF-токен).
TokenAuthentication
Простой постоянный токен для каждого пользователя:
INSTALLED_APPS += ["rest_framework.authtoken"]
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
],
...
}
python manage.py migrate
python manage.py drf_create_token admin
Получение токена по логину и паролю:
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = [
path("api-token-auth/", obtain_auth_token),
]
curl -X POST -d "username=admin&password=secret" http://127.0.0.1:8000/api/api-token-auth/
# {"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"}
curl -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" \
-X POST -H "Content-Type: application/json" \
-d '{"title": "Пост из API", "slug": "post-iz-api", "body": "Текст"}' \
http://127.0.0.1:8000/api/posts/
JWT (JSON Web Token)
Стандарт для SPA и мобильных приложений — пакет djangorestframework-simplejwt:
pip install djangorestframework-simplejwt
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
}
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path("token/", TokenObtainPairView.as_view()),
path("token/refresh/", TokenRefreshView.as_view()),
]
Клиент получает пару: короткоживущий access (минуты) и долгоживущий refresh (дни). Запросы идут с Authorization: Bearer <access>; когда access истёк, клиент получает новый по refresh. JWT подписан сервером и содержит id пользователя, поэтому проверяется без запроса к базе.
| Token | JWT | |
|---|---|---|
| Хранение | В базе | Не нужно (подпись) |
| Срок жизни | Бессрочный | Ограниченный |
| Отзыв | Удалить из базы | Сложнее (blacklist) |
Только HTTPS
Токен — это ключ от аккаунта. Передавайте его только по HTTPS и никогда в адресе запроса.
Ограничение частоты запросов (throttling)
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {"anon": "100/day", "user": "1000/day"},
}
При превышении — ответ 429 Too Many Requests. Защищает от перебора паролей и злоупотреблений.
Итог
- Аутентификация определяет
request.user, права решают, пускать ли запрос. IsAuthenticatedOrReadOnly+ своё объектное правоIsAuthorOrReadOnly— типичная схема для блога.- Токены:
TokenAuthentication(просто) или JWT (simplejwt, access/refresh). - Throttling ограничивает частоту запросов.
Практика решено 0 из 6
Какое право разрешает читать всем, а изменять — только аутентифицированным пользователям?
Напишите право IsAuthorOrReadOnly (наследник permissions.BasePermission): метод
has_object_permission(self, request, view, obj) разрешает безопасные методы (permissions.SAFE_METHODS) всем,
а остальные — только если obj.author == request.user.
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
Примените к PostViewSet права: IsAuthenticatedOrReadOnly и ваш IsAuthorOrReadOnly.
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsAuthorOrReadOnly]
Как клиент передаёт токен DRF (TokenAuthentication) в запросе?
Подключите endpoint получения токена по логину и паролю: маршрут api-token-auth/
со встроенным представлением obtain_auth_token.
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = [
path("api-token-auth/", obtain_auth_token, name="api_token_auth"),
]
Чем JWT отличается от простого токена DRF (TokenAuthentication)?
Войдите, чтобы отмечать пройденные уроки.