⚖️ Старый vs Новый — Урок 40

← К оглавлению урока

⚡ Главные изменения в подходах

  • Старый подход: TokenAuthentication как основа без срока действия токенов
  • Новый подход: JWT с access/refresh токенами — токены имеют срок действия, ротация refresh
  • Старый: создание токена вручную через shell или сигналы
  • Новый: TokenObtainPairView / obtain_auth_token — стандартные эндпоинты
  • Старый: rest_framework.authtoken — один токен навсегда
  • Новый: djangorestframework-simplejwt — stateless, масштабируемость

1. Создание токена: старый vs новый способ

Из лекции (старый подход)

# Создание токена программно при регистрации
# example.py
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import User

# Создаем пользователя и токен вручную
user = User.objects.create(
    username='newuser',
    password='newpassword'  # ← небезопасно!
)
token, created = Token.objects.get_or_create(user=user)
print(token.key)

Современный подход (Django 5 / DRF 3.15+)

# Создание через сигнал — автоматически при регистрации
# signals.py
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token

@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)

# apps.py — регистрация сигнала
class MyAppConfig(AppConfig):
    name = 'myapp'

    def ready(self):
        import myapp.signals  # noqa
Почему важно: User.objects.create(password='...') сохраняет пароль в открытом виде. Всегда используйте create_user() — он хеширует пароль.

2. TokenAuthentication vs JWT — выбор архитектуры

Из лекции (TokenAuthentication)

# Один токен на пользователя
# Хранится в БД
# Без срока действия
# Нужен запрос к БД при каждой проверке

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ],
}

INSTALLED_APPS = [
    ...
    'rest_framework.authtoken',
]

Современный (Simple JWT)

# Access + Refresh токены
# Stateless — не нужна БД для проверки
# Access: 5 минут, Refresh: 1 день
# Ротация refresh при желании

# settings.py
from datetime import timedelta

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': True,   # ← безопаснее
    'BLACKLIST_AFTER_ROTATION': True,
}
Критерий TokenAuthentication (DRF) Simple JWT
Хранение В базе данных Stateless (только подпись)
Срок действия Нет (вечный) Короткий access + долгий refresh
Запрос к БД при проверке Каждый запрос Нет (только verif. подписи)
Масштабируемость Ограничена БД Высокая (stateless)
Отзыв токена Удаление из БД Blacklist или короткий TTL
Установка Встроен в DRF pip install djangorestframework-simplejwt

3. Получение токена: старый vs новый URL-паттерн

Из лекции

# urls.py
from rest_framework.authtoken.views import obtain_auth_token

urlpatterns = [
    path('api-token-auth/', obtain_auth_token),
]

# Ответ: {"token": "abc123..."}

Современный (JWT)

# urls.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView,  # дополнительно
)

urlpatterns = [
    path('api/token/', TokenObtainPairView.as_view()),
    path('api/token/refresh/', TokenRefreshView.as_view()),
    path('api/token/verify/', TokenVerifyView.as_view()),
]

# Ответ: {"access": "eyJ...", "refresh": "eyJ..."}

4. Глобальная настройка разрешений

Старый подход (не рекомендуется)

# Нет глобальной настройки —
# разрешения задаются в каждом view вручную

class MyView(APIView):
    permission_classes = [IsAuthenticated]
    ...

class AnotherView(APIView):
    permission_classes = [IsAuthenticated]
    ...
# Повторение кода, легко забыть

Современный подход

# settings.py — один раз глобально
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

# Переопределение только там, где нужно исключение
class PublicView(APIView):
    permission_classes = [AllowAny]  # ← явное исключение
    ...
Рекомендация DRF 3.15+: устанавливайте по умолчанию строгие разрешения (IsAuthenticated), а для публичных эндпоинтов явно указывайте AllowAny. Это принцип «безопасно по умолчанию» (secure by default).