✅ Решения: самопроверка 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

КлассСценарийЗаголовок
SessionAuthenticationDjango web-приложения с формамиCookie + CSRF
BasicAuthenticationТестирование и разработкаAuthorization: Basic base64(user:pass)
TokenAuthenticationМобильные приложения, SPAAuthorization: Token <key>
RemoteUserAuthenticationSSO / корпоративная аутентификация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

  1. Регистрация: POST /api/register/ → RegisterSerializer.create_user() → RefreshToken.for_user() → set_cookie(access, refresh, httponly=True) → 201
  2. Запрос к API: GET /api/books/ → JWTAuthenticationMiddleware читает куки → добавляет Authorization: Bearer <access> → JWTAuthentication проверяет → IsAuthenticated разрешает
  3. Истечение access: API возвращает 401 → клиент делает POST /api/token/refresh/ с refresh из куки → получает новый access
  4. Logout: POST /api/logout/ → delete_cookie(access_token, refresh_token) → если BLACKLIST включён: старый refresh в blacklist → пользователь вышел

Компоненты: RegisterView, JWTAuthenticationMiddleware, TokenRefreshView (SimpleJWT), LogoutView, BlacklistMixin (rest_framework_simplejwt.token_blacklist).