Аутентификация Django не работает с nginx и gunicorn, но работает с run_server
Я прочитал бесчисленное количество статей и перечитал эту проблему, но у меня пока нет ответа. У меня есть стандартная система, запущенная в docker с помощью docker-compose, которая имеет nginx в качестве обратного прокси для gunicorn и стандартную систему аутентификации django. Я переделал страницы входа, установив классы форм, но это все, что я могу настроить.
Когда я запускаю свой сайт в run_server для его отладки, все работает. Я перехожу на страницу входа, успешно вхожу и получаю перенаправление. is_authenticated дает исключительные результаты. Я запускаю точно такие же страницы на nginx и gunicorn и получаю очень странное поведение. Часто я успешно вхожу в систему, мой sessionid совпадает с сессией в базе данных, токен csrf совпадает с тем, что находится в базе данных, но is_authenticated оказывается ложным. Но только около 90% времени. Часто я попадаю на страницу, которая требует входа, используя декоратор login_required на url (login_required(view.as_view())), и мне приходится входить несколько раз, is_authenticated false каждый раз, пока это не сработает, а когда это сработает, is_authenticated устанавливается в false, как только я перехожу на страницу входа.
Вот моя конфигурация nginx. Обратите внимание, что django - это ip-адрес, назначенный docker-compose, а gunicorn работает на порту 8050. Это действительно стандартно для этого:
server {
listen 80;
server_name _;
#charset koi8-r;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
#location / {
# root /usr/share/nginx/html;
# index index.html index.htm;
#}
location /static/ {
autoindex on;
alias /static/;
}
location /pgadmin4/ {
# forward application requests to the gunicorn server
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Script-Name /pgadmin4;
# Changing timeout behavior
proxy_read_timeout 300;
proxy_pass http://pgadmin;
}
location / {
# forward application requests to the gunicorn server
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Changing timeout behavior
proxy_read_timeout 300;
proxy_pass http://django:8050;
}
}
Вот соответствующая часть settings.py
INSTALLED_APPS = [
'cvi.apps.CviConfig',
'crispy_forms',
'notes.apps.NotesConfig',
'scenarios.apps.ScenariosConfig',
'tsmodels.apps.TsmodelsConfig',
'services.apps.ServicesConfig',
'model_info.apps.ModelInfoConfig',
'data_tools.apps.DataToolsConfig',
'demos.apps.DemosConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'bootstrap_datepicker_plus',
'wkhtmltopdf'
]
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',
]
WSGI_APPLICATION = 'DataManagement.wsgi.application'
А вот скрипт запуска gunicorn:
exec gunicorn DataManagement.wsgi:application \
--name=DataManagement \
--workers=4 \
--log-level=debug \
--bind=0.0.0.0:8050 \
--timeout=600 \
--log-file=./gunicorn.log \
--log-level=debug
Эта конфигурация работает без аутентификации. Второй раз я добавил login_required к моим урлам, когда возникла проблема с аутентификацией. Я буду очень признателен за любую помощь, которую вы можете оказать. Я никогда не сталкивался с этой проблемой раньше, когда настраивал аутентификацию пользователей на других сайтах django.
Проблема заключается в отсутствии документации и вариантов конфигурации. Мы использовали SECRET_KEY в django, очевидно, не скопированный сюда. Согласно документации, у нас есть get_random_secret_key() для среды разработки. Это не работает с несколькими рабочими в gunicorn. Каждый рабочий будет запускать новый терминал и, следовательно, получать новый SECRET_KEY для каждого рабочего. Секретный ключ, очевидно, используется где-то в системе аутентификации django. У вас нет контроля над тем, какой рабочий получает ваш запрос от nginx, поэтому возможно, что у вас один рабочий для 3 или 4 ваших запросов или 4 рабочих для ваших 4 запросов. Есть несколько вариантов, как это исправить.
- Используйте фиксированный секретный ключ. Рабочие запускаются с одним и тем же ключом, и это, похоже, работает. .
- Используйте один рабочий с несколькими потоками и gthread в качестве рабочего класса gunicorn. Нити одного рабочего будут наследовать секретный ключ. .
Это не документировано в django или gunicorn, и рабочие примеры по умолчанию будут неудачными. Вот пример команды gunicorn, которую я использую для запуска контейнера django:
exec gunicorn DataManagement.wsgi:application \
--name=DataManagement \
--workers=1 \
--log-level=debug \
--bind=0.0.0.0:8050 \
--timeout=600 \
--log-file=./gunicorn.log \
--log-level=debug \
--worker-class=gthread \
--threads=16
`