✅ Решения: самопроверка Auth-блока
⚡ Ключевые ответы
- Аутентификация: 401 при неудаче; авторизация: 403 при неудаче.
- JWT: header (тип+алгоритм) . payload (claims: sub, iat, exp) . signature.
- has_object_permission() вызывается ПОСЛЕ has_permission() при get_object().
- AppConfig.ready() гарантирует, что все модели загружены перед импортом сигналов.
- Access (5 мин) — для API-запросов; refresh (1 день) — для получения нового access.
Ответы на вопросы
Ответ 1: Аутентификация vs авторизация
Аутентификация — «кто ты?» — проверяет личность пользователя. DRF перебирает authentication_classes → устанавливает request.user. При неудаче: 401 Unauthorized.
Авторизация — «что тебе можно?» — проверяет права. DRF вызывает permission_classes. При неудаче: 403 Forbidden.
Порядок обязателен: сначала аутентификация, потом авторизация. Нельзя проверить права, не зная, кто пользователь.
Ответ 2: Классы аутентификации DRF
| Класс | Сценарий | Заголовок |
|---|---|---|
| SessionAuthentication | Django web-приложения с формами | Cookie + CSRF |
| BasicAuthentication | Тестирование и разработка | Authorization: Basic base64(user:pass) |
| TokenAuthentication | Мобильные приложения, SPA | Authorization: Token <key> |
| RemoteUserAuthentication | SSO / корпоративная аутентификация | REMOTE_USER заголовок |
Ответ 3: Настройка TokenAuthentication
INSTALLED_APPS = [
'rest_framework',
'rest_framework.authtoken', # создаёт таблицу authtoken_token в БД
]
# python manage.py migrate — создаёт таблицу
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
}
'rest_framework.authtoken' нужен, потому что Token — это отдельная модель с таблицей в БД. Без migrate таблица не создастся и Token.objects.create() упадёт с ошибкой.
Ответ 4: Структура JWT
JWT состоит из трёх частей, разделённых точкой: eyJhbGci...<header>.eyJzdWIi...<payload>.SflKx...<signature>
- header — тип токена (JWT) и алгоритм подписи (HS256). Base64url.
- payload — claims (утверждения):
sub(идентификатор пользователя),iat(время выпуска),exp(время истечения) + кастомные поля. - signature — HMAC(header + payload, SECRET_KEY). Гарантирует целостность.
Claims — это ключ-значение пары в payload. Нельзя хранить чувствительные данные (пароль) — payload декодируется без ключа.
Ответ 5: Access vs Refresh токен
Access-токен: короткий срок действия (5 минут по умолчанию). Используется для авторизации API-запросов. Клиент добавляет в заголовок: Authorization: Bearer <access>.
Refresh-токен: длинный срок (1 день). Используется только для получения нового access-токена через /api/token/refresh/. Хранится безопаснее (например, в httpOnly-куки).
Ротация (ROTATE_REFRESH_TOKENS=True): при каждом обновлении access-токена выдаётся новый refresh-токен, а старый добавляется в blacklist. Защищает от кражи refresh-токена.
Ответ 6: Встроенные разрешения
AllowAny, IsAuthenticated, IsAdminUser (is_staff=True), IsAuthenticatedOrReadOnly.
Неаутентифицированный пользователь с IsAuthenticated → DRF вернёт 401 Unauthorized. Если аутентифицирован, но нет прав → 403 Forbidden.
Ответ 7: Объектный уровень разрешений
Разрешения на уровне объекта позволяют проверять права не для всего view, а для конкретного экземпляра модели.
DRF вызывает has_object_permission(request, view, obj) когда view вызывает get_object() (при GET /resource/{id}/, PUT, PATCH, DELETE).
Важно: has_object_permission() вызывается ТОЛЬКО если has_permission() уже вернул True.
Ответ 8: Назначение владельца через request.user
class BookViewSet(ModelViewSet):
queryset = Book.objects.all()
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
# Автоматически добавляет текущего пользователя как владельца
serializer.save(owner=self.request.user)
Поле owner в сериализаторе должно быть read_only=True, чтобы пользователь не мог подменить его в теле запроса.
Ответ 9: DjangoModelPermissions
DjangoModelPermissions проверяет Django-разрешения модели: add, change, delete, view. Пользователь должен иметь соответствующее разрешение для выполнения HTTP-метода.
IsAuthenticated просто проверяет, аутентифицирован ли пользователь. DjangoModelPermissions дополнительно проверяет конкретные права.
Базовые разрешения Django Admin для модели: view, add, change, delete.
Ответ 10: Сигналы Django
Основные сигналы: pre_save, post_save, pre_delete, post_delete, m2m_changed.
Аргументы post_save: sender (класс модели), instance (объект), created (bool — True при первом создании), raw, using, update_fields.
created=True только при первом вызове save() (INSERT), при UPDATE — False.
Ответ 11: AppConfig.ready()
Сигналы нужно регистрировать в AppConfig.ready(), потому что этот метод вызывается после того, как Django загрузил все модели всех приложений. Импорт сигналов в models.py может привести к circular imports (models.py импортирует signals.py, который импортирует models.py).
class FirstAppConfig(AppConfig):
name = 'first_app'
def ready(self):
import first_app.signals # безопасно — всё уже загружено
Ответ 12: Email при создании объекта
# settings.py (разработка)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# signals.py
from django.core.mail import send_mail
@receiver(post_save, sender=Book)
def notify_on_create(sender, instance, created, **kwargs):
if created:
send_mail(
'New Book',
f'Book {instance.title} created.',
'admin@example.com',
['admin@example.com'],
)
Консольный бэкенд выводит всё письмо в терминал — удобно для разработки и тестирования без реального SMTP.
Ответ 13: Автосоздание токена через сигнал
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import User
@receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
Теперь токен создаётся автоматически при любом создании пользователя (через shell, admin, API). Не нужно помнить создавать его вручную в каждом view.
Ответ 14: Swagger
pip install drf-yasg
# settings.py — добавить 'drf_yasg' в INSTALLED_APPS
# urls.py
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework import permissions
schema_view = get_schema_view(
openapi.Info(title="API", default_version='v1'),
public=True,
permission_classes=[permissions.AllowAny],
)
urlpatterns += [
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0)),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0)),
]
Ответ 15: Полный цикл JWT
- Регистрация: POST /api/register/ → RegisterSerializer.create_user() → RefreshToken.for_user() → set_cookie(access, refresh, httponly=True) → 201
- Запрос к API: GET /api/books/ → JWTAuthenticationMiddleware читает куки → добавляет Authorization: Bearer <access> → JWTAuthentication проверяет → IsAuthenticated разрешает
- Истечение access: API возвращает 401 → клиент делает POST /api/token/refresh/ с refresh из куки → получает новый access
- Logout: POST /api/logout/ → delete_cookie(access_token, refresh_token) → если BLACKLIST включён: старый refresh в blacklist → пользователь вышел
Компоненты: RegisterView, JWTAuthenticationMiddleware, TokenRefreshView (SimpleJWT), LogoutView, BlacklistMixin (rest_framework_simplejwt.token_blacklist).