⚖️ Старый 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).