LINUX.ORG.RU

flask через uwsgi теряет сессию залогиненого юзера если много uwsgi процессов.

 , ,


0

1

Привет.

Запускаю flask через uwsgi, авторизация пользователя через ldap, с UserMixin.

Если в конфиге uwsgi количество процессов больше одного ( у меня 5), то похоже, что каждый процесс требует, чтобы пользователь залогинился именно в нем.

Те я логинюсь и меня переносит на страницу с @flask_login.login_required. Затем я для теста обновляю страницу и с какой-то вероятностью меня вышибает назад, так как юзер не залогинен. Но если провести эту итерацию 5-8 раз, то все начинает работать хорошо.

Если же уменьшить количество процессов uwsgi до одного, то все сразу работает как часы.

★★★

Я не очень разбираюсь в фласке, но посмотри, как в нём работает сессия. Есть вероятность, что каждый процесс хранит сессии отдельно (в своей собственной оперативной памяти может быть?).

eternal_sorrow ★★★★★ ()
Ответ на: комментарий от eternal_sorrow

Есть вероятность, что каждый процесс хранит сессии отдельно

Ну что-то такое явно и происходит. Но я еще более нифига не разбираюсь, это один из моих тестовых проектов.

stackoverflow говорит, что проблема может быть из-за генерации SECRET_KEY, но он у меня в данном случае статичный.

constin ★★★ ()

ЕМНИП, flask же не хранит сессию на сервере - всё хранится в куке, подлинность которой обеспечивается SECRET_KEY. Соответсвенно, если тебя разлогинивает, либо у тебя неправильно хранятся куки, либо их переписывает приложение.

slovazap ★★★★★ ()
Ответ на: комментарий от slovazap

Видел давным-давно одну веб-макаку, которая SECRET_KEY генерировала динамически из random, вместо статического значения из, скажем, конфига. У нее бы точно так же сломалось.

AEP ★★★★★ ()
Ответ на: комментарий от slovazap

. Соответсвенно, если тебя разлогинивает, либо у тебя неправильно хранятся куки, либо их переписывает приложение.

Имхо было бы верно, если бы все глючило при одном процессе, но при одном процессе все ок.

constin ★★★ ()
Ответ на: комментарий от maxim2

про ldap с UserMixin, поподробней.

как-то так:

models.py

import flask_login
from flask_ldap3_login import LDAP3LoginManager
from flask_login import LoginManager, login_user, UserMixin, current_user
from flask_ldap3_login.forms import LDAPLoginForm
...


class User(UserMixin):
    def __init__(self, dn, username, data):
        self.dn = dn
        self.username = username
        self.data = data
        self.displayName = data['displayName']
        user_email = username[0] + '.' + username[1:] + '@' + app.config.get('EMAIL_DOMAIN')
        self.mail = user_email

    def __repr__(self):
        return self.dn

    def get_id(self):
        return self.dn


@login_manager.user_loader
def load_user(id):
    if id in users:
        return users[id]
    return None


@ldap_manager.save_user
def save_user(dn, username, data, memberships):
    if data.get("memberOf"):
        #if current_app.config.get('ACCESS_GROUP') in data.get("memberOf"):
        if app.config['ACCESS_GROUP'] in data.get("memberOf"):
            user = User(dn, username, data)
            users[dn] = user
            return user

....

__init.py__



from flask import Flask
from flask_ldap3_login import LDAP3LoginManager
from flask_login import LoginManager


app = Flask(__name__)

....

app.config['LDAP_HOST'] = 'xxxx.xxxx.xxxx'
app.config['LDAP_PORT'] = 636
app.config['LDAP_BASE_DN'] = 'dc=xxxx,dc=xxx'
app.config['LDAP_USER_DN'] = 'cn=Users'
app.config['LDAP_GROUP_DN'] = 'cn=Groups'
app.config['LDAP_USER_RDN_ATTR'] = 'cn'
app.config['LDAP_USER_LOGIN_ATTR'] = 'cn'
app.config['LDAP_BIND_USER_DN'] = 'cn=ldapsearch,cn=users,dc=xxxxx,dc=xxxx'
app.config['LDAP_GROUP_OBJECT_FILTER'] ='(objectclass=group)'
app.config['LDAP_BIND_USER_PASSWORD'] = 'xxxxxx'
app.config['LDAP_USE_SSL'] = True

login_manager = LoginManager(app)             
ldap_manager = LDAP3LoginManager(app) 

...

router.py


....

@app.route("/logout")
@flask_login.login_required
def logout():
    flask_login.logout_user()
    return redirect('/login')


@app.route('/login', methods=['GET', 'POST'])
def login():
    login_form = LDAPLoginForm()

    if login_form.validate_on_submit():
        # Successfully logged in, We can now access the saved user object
        # via form.user.
        if login_form.user:
            login_user(login_form.user)
            return redirect('/newuser') 
        #login_user(form.user)  # Tell flask-login to log them in.
        else:
            flash('Login Failed', 'warning')
            return redirect('login')
        return redirect('/') 

    return render_template('login.html', login_form=login_form)
constin ★★★ ()
Последнее исправление: constin (всего исправлений: 1)
Ответ на: комментарий от constin

Возможно у тебя в процессах хранится ещё какое-то состояние, и оно переписывает сессию. У меня uwsgi 3 процесса, голый flask, авторизация вида if flask.request.form.get('password') == config.password и ничего не разлогинивается.

Видел давным-давно одну веб-макаку, которая SECRET_KEY генерировала динамически из random, вместо статического значения из, скажем, конфига. У нее бы точно так же сломалось.

Ну вроде выше было явно сказано что

проблема может быть из-за генерации SECRET_KEY, но он у меня в данном случае статичный.

На всяких случай уточню - точно одинаковый на все процессы?

slovazap ★★★★★ ()
Ответ на: комментарий от slovazap

На всяких случай уточню - точно одинаковый на все процессы?

ну было :


app.config.from_object(Config)
...


import os

class Config(object):
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'


Сейчас стал внедрять Flask-Session и он такое кушать не стал, поэтому ключ теперь через app.config.

Но теперь при запуске напрямую все работает, при запуске через uwsgi он вообще не логинит. Завтра выясню как включять debug:)

constin ★★★ ()
Ответ на: комментарий от maxim2

я бы начал с

и как это относится к проблеме? я это отдебажил перед тем как писать пост, там анонимный неаутифицированный юзер остается из-за перескока на другой процесс.( с вероятностью количество процессов минус 1 к количеству процессов). О чем собственно и пост.

да и меня один фиг роутами туда кидает, только если юзер не юзер

@app.route('/')
def home():
    # Redirect users who are not logged in.
    if not current_user or current_user.is_anonymous:
        return redirect(url_for('login'))

    return redirect(url_for('task'))

и куда будет выводиться print?:)

ну и вообще, так вроде не пишут print

лучше так:

print(f'Curent User is auth: {flask_login.curent_user.is_authenticated}')

или так:

print('Curent User is auth: {}'.format( flask_login.curent_user.is_authenticated))

constin ★★★ ()
Последнее исправление: constin (всего исправлений: 3)
Ответ на: комментарий от maxim2

Вангую что в uwsgi переменная users для каждого экземпляра своя.

очень похоже!!

попробовал показывать users на странице логина. при попадании на «новый» незалогиненый воркер словарь users - пустой.

constin ★★★ ()