🐛 Типичные ошибки — Урок 44
⚡ Топ-5 ошибок
- Нет token_blacklist в INSTALLED_APPS → ImproperlyConfigured при BLACKLIST_AFTER_ROTATION=True
- Нет миграций после добавления token_blacklist → OperationalError: no such table
- secure=True на localhost → куки не устанавливаются без HTTPS
- create() вместо create_user() → пароль в открытом виде, аутентификация не работает
- Нет AllowAny на LoginView/RegisterView → 403 для всех неавторизованных запросов
Распространённые ошибки при работе с JWT в DRF
Ошибка 1: Отсутствие token_blacklist в INSTALLED_APPS
Симптом:
ImproperlyConfigured: 'BLACKLIST_AFTER_ROTATION': True requires 'rest_framework_simplejwt.token_blacklist' in INSTALLED_APPS
# Неверно — token_blacklist не добавлен
INSTALLED_APPS = [
'rest_framework_simplejwt',
# ...
]
SIMPLE_JWT = {
'BLACKLIST_AFTER_ROTATION': True, # Требует token_blacklist!
}
# Верно
INSTALLED_APPS = [
'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist', # Добавить
# ...
]
После добавления обязательно выполнить: python manage.py migrate
Ошибка 2: Забыть выполнить миграции
Симптом:
OperationalError: no such table: token_blacklist_blacklistedtoken
# После добавления token_blacklist в INSTALLED_APPS
python manage.py migrate
# Проверить наличие таблиц
python manage.py showmigrations token_blacklist
Ошибка 3: secure=True на localhost
Симптом: куки не устанавливаются, в DevTools видно Set-Cookie без сохранения, браузер игнорирует куку.
# Неверно для разработки
response.set_cookie(
key='access_token',
value=str(access_token),
secure=True, # Требует HTTPS — не работает на http://localhost
)
# Верно для разработки
import os
IS_PROD = os.environ.get('DJANGO_ENV') == 'production'
response.set_cookie(
key='access_token',
value=str(access_token),
secure=IS_PROD, # False в dev, True в prod
httponly=True,
samesite='Lax',
)
Ошибка 4: create() вместо create_user() в сериализаторе
Симптом: пользователь создаётся, но аутентификация не работает.
authenticate() возвращает None.
# Неверно — пароль сохраняется в открытом виде
def create(self, validated_data):
user = User.objects.create(**validated_data) # Пароль не хэшируется!
return user
# Верно — create_user() хэширует пароль через set_password()
def create(self, validated_data):
user = User.objects.create_user(
username=validated_data['username'],
password=validated_data['password'],
email=validated_data.get('email', '')
)
return user
Django хранит пароли в формате {algorithm}${iterations}${salt}${hash}. При вызове create() напрямую пароль остаётся как есть, и authenticate() не сможет сравнить хэш.
Ошибка 5: Отсутствие AllowAny на LoginView и RegisterView
Симптом:
403 Forbidden при попытке залогиниться или зарегистрироваться — ещё до ввода данных.
# Неверно — если DEFAULT_PERMISSION_CLASSES = IsAuthenticated
class LoginView(APIView):
# permission_classes не указан → наследует IsAuthenticated
def post(self, request):
...
# Верно — явно разрешаем доступ анонимам
from rest_framework.permissions import AllowAny
class LoginView(APIView):
permission_classes = [AllowAny] # Обязательно!
def post(self, request):
...
class RegisterView(APIView):
permission_classes = [AllowAny] # Обязательно!
def post(self, request):
...
Ошибка 6: Хранение токенов в localStorage (небезопасно)
Риск безопасности: XSS-атака может прочитать
localStorage и похитить токены.
// Неверно — небезопасное хранение
localStorage.setItem('access', response.data.access);
// Верно — httpOnly-куки (устанавливает сервер)
// На фронтенде ничего не нужно — браузер управляет куками автоматически
fetch('/api/data/', { credentials: 'include' });
Ошибка 7: Незащищённый LogoutView
Симптом: после логаута refresh-токен остаётся валидным до истечения срока (1 день).
# Неполный вариант из лекции — только удаляет куки
class LogoutView(APIView):
def post(self, request):
response = Response(status=status.HTTP_204_NO_CONTENT)
response.delete_cookie('access_token')
response.delete_cookie('refresh_token')
return response
# Полный вариант — с инвалидацией refresh-токена
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.exceptions import TokenError
class LogoutView(APIView):
def post(self, request):
refresh_token_str = request.COOKIES.get('refresh_token')
if refresh_token_str:
try:
RefreshToken(refresh_token_str).blacklist()
except TokenError:
pass # Уже истёк — нечего делать
response = Response(status=status.HTTP_204_NO_CONTENT)
response.delete_cookie('access_token')
response.delete_cookie('refresh_token')
return response
Ошибка 8: Неправильный порядок middleware
Симптом: токены не обрабатываются, хотя middleware подключён.
# Неверно — JWTAuthenticationMiddleware стоит ПЕРЕД SessionMiddleware
MIDDLEWARE = [
'myapp.middleware.JWTAuthenticationMiddleware', # Слишком рано!
'django.contrib.sessions.middleware.SessionMiddleware',
# ...
]
# Верно — JWTAuthenticationMiddleware после встроенных middleware
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'myapp.middleware.JWTAuthenticationMiddleware', # В конце
]