Получение сообщения "CSRF-токен отсутствует или неверен" при запросе на получение токена
Мы используем Django REST Framework и используем логины пользователей. Из веб-клиента у нас есть экран входа и мы используем obtain_auth_token
из REST Framework для получения api токена. Веб-клиент использует XMLHttpRequest
.
В начале все работает нормально. Веб-клиент получает токен, используя имя пользователя+пароль, и использует его в следующих вызовах API.
Когда я возвращаюсь на следующий день, открываю новую вкладку браузера и пытаюсь войти в систему, я получаю 403 Forbidden
, а в логах Django (и в теле ответа) написано {"detail":"CSRF Failed: CSRF token missing or incorrect."}
Я вижу, что входящий запрос имеет куки csrftoken
и куки sessionid
. Я вижу те же куки, если я использую браузер "Инструменты разработчика". Если я удалю эти два куки, все работает нормально.
Кроме того, если я запускаю частное окно браузера (= инкогнито), веб-приложение работает нормально.
Я не знаю, почему появляются эти куки, когда именно они появляются и почему фреймворк REST не любит их.
У меня есть два подозрения:
- We also use the Django admin interface. Could it be that the login to the admin interface on the same domain will plant those cookies and somehow interfere with the REST Framework?
- Something about time passes will make the problem appear? It seems to me that the problem does not appear until "the day after" if I clear the cookies. This might very well be other circumstances tricking me, like point 1 above.
Есть предложения, как решить эту проблему?
Для справки, некоторые фрагменты нашего Django setting.py
:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'request_logging.middleware.LoggingMiddleware',
]
INSTALLED_APPS = [
'mybackend.apps.MybackendConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_cleanup.apps.CleanupConfig',
'rest_framework',
'rest_framework.authtoken',
'adminsortable',
'corsheaders',
'django_filters',
'storages',
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissions'
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
}
От urls.py
:
from rest_framework.authtoken import views as restviews
...
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^api/token-auth/obtain_auth_token', restviews.obtain_auth_token),
url(r'^api/', include(router.urls)),
]
Похоже, что виновником является SessionAuthentication
. Я предполагаю, что он был добавлен, чтобы иметь возможность играть с REST-интерфейсом из браузера в целях тестирования.
Если я удалю SessionAuthentication
, то, кажется, все работает.
Теперь конфигурация выглядит следующим образом:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissions'
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
}