Как выйти из базы данных пользователя с помощью flask-login

У меня есть веб-приложение, созданное с помощью python / flask, и я использую flask-login для аутентификации пользователей. В пользовательском интерфейсе пользователь может выйти из системы. Но пользователь также может выйти из системы с помощью базы данных (по истечении сеанса или по истечении срока действия пользователя).

Мой пользовательский загрузчик выглядит так:

@login_manager.user_loader
def user_loader(id):
    user = SessionUser.find_by_session_id(id) #hits the database
    if user is None:
        flash('You have been automatically logged out')
        #flask_login.logout_user() #RecursionError: maximum recursion depth exceeded
        session['user_id'] = None
    return user

Когда база данных не может найти id, я хочу, чтобы пользователь вышел из системы. Причины: нет необходимости нажимать SessionUser.find_by_session_id-call более одного раза, если id не может быть найден. Кроме того, я хочу показать сообщение, что пользователь вышел из системы (и я хочу показать это только один раз). Наконец, когда пользователь выходит из базы данных, я хочу использовать неаутентифицированные шаблоны.

Если у меня нет кода if user is None:, пользовательский загрузчик попадал в него при каждом запросе. С этим есть две проблемы:

  1. Я не могу высветить сообщение "Вы были автоматически отключены" (потому что оно будет продолжать отображаться)
  2. Произошло ненужное попадание в базу данных.

Я исправил это, очистив переменную сеанса user_id, но это нежелательное решение, поскольку оно использует внутренние компоненты flask_login, а не API.

Какой будет лучший подход?


person doekman    schedule 06.12.2017    source источник


Ответы (2)


Как сказал @Sraw, не выводите пользователей из user_loader вручную. Это должно возвращать только пользователя с заданным идентификатором или None:

@login_manager.user_loader
def user_loader(user_id):
    return SessionUser.find_by_session_id(user_id)  # hits the database

Вышеупомянутое отвечает на ваш вопрос:

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

Теперь, чтобы ответить на ваш второй вопрос:

Я хочу показать сообщение, что пользователь вышел из системы (и я хочу показать это только один раз)

Вот способы выхода пользователя из системы:

  1. user_id отсутствует в вашей базе (пользователь был удален)
  2. user_id не находится в сеансе cookie браузера пользователя (еще не вошел в систему и не очистил свои файлы cookie)
  3. Вы позвонили flask_login.logout_user, что удаляет user_id из session

Вот работающее мини-приложение, которое отображает сообщение, когда пользователь выходит из системы:

from flask import current_app as app, flash, redirect, render_template, session
from flask_login import login_manager, login_required, logout_user


@login_manager.user_loader
def user_loader(user_id):
    return SessionUser.find_by_session_id(user_id)


@app.route('/logout')
@login_required
def logout():
    logout_user()
    if session.get('was_once_logged_in'):
        # prevent flashing automatically logged out message
        del session['was_once_logged_in']
    flash('You have successfully logged yourself out.')
    return redirect('/login')


@app.route('/login', methods=['GET', 'POST'])
def login():
    if app.current_user.is_authenticated:  # already logged in
        return redirect('/home')
    if request.method == 'POST':
        user = SessionUser.find_by_session_id(request.data['user_id'])
        if user:
            login_user(user)
            session['was_once_logged_in'] = True
            return redirect('/home')
        flash('That user was not found in the database.')
    if session.get('was_once_logged_in'):
        flash('You have been automatically logged out.')
        del session['was_once_logged_in']
    return render_template('/login.html')


@app.route('/home')
@login_required
def home():
    return 'You are logged in as {0}.'.format(app.current_user.id)

Содержание login.html:

{% with messages = get_flashed_messages() %}
  {% if messages %}
    <ul class=flashes>
    {% for message in messages %}
      <li>{{ message }}</li>
    {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

<form method="post">
  User ID: <input type="text" name="user_id" /><br />
  <button type="submit">Login</button>
</form>
person Alan Hamlett    schedule 06.12.2017
comment
Это решение может сработать, потому что по истечении сеанса вы всегда переходите на страницу входа. Я также понимаю, что мне нужно немного глубже задуматься над этой проблемой. Когда база данных возвращает None, это может быть связано с тем, что истек срок действия db-session или db-useraccount. Кроме того, когда вы используете flask.session.permanent=True, возникает дополнительный случай, почему пользователю необходимо войти в систему. Также: мне все еще не нравится ситуация, когда user_loader возвращает None, переменные сеанса продолжают существовать. Я думаю, что Flask-Login должен поддерживать какой-то неявный выход из системы, который можно вызвать из пользовательского загрузчика. - person doekman; 07.12.2017
comment
Я не думаю, что введение дополнительной переменной сеанса - хорошая идея. Почему так важно не удалять user_id из сеанса? - person doekman; 20.02.2018
comment
@doekman Сеанс Flask - это то, как вы отправляете сообщения пользователю, и это неплохо. Не храните данные пользователя в файлах cookie, тогда вам не нужно беспокоиться об очистке файлов cookie / сеанса при выходе из системы. Если вы действительно должны удалить cookie сеанса пользователя, просто сделайте это, когда строка после вызова logout_user(). - person Alan Hamlett; 21.02.2018
comment
Мы явно не видим точки друг друга. Думаю, я оставлю все как есть. - person doekman; 21.02.2018

Я не думаю, что вам нужно вручную выходить из системы, если срок его сеанса в базе данных истек.

Проверьте это.

Как видите, если user_loader возвращает None, это означает, что этот пользователь недействителен, поэтому он будет удален из текущего сеанса, который совпадает с logout. Итак, что вам нужно сделать, это просто return SessionUser.find_by_session_id(id).

Обновлять

Следующий фрагмент должен работать должным образом. Если нет, то может быть что-то еще не так.

@login_manager.user_loader
def user_loader(id):
    # print(id)  # try printing current user's id to check.
    user = SessionUser.find_by_session_id(id) #hits the database
    if user is None:
        flash('You have been automatically logged out')
    return user
person Sraw    schedule 06.12.2017
comment
Если я правильно понимаю ваш ответ, когда вы вызываете @login_manager.user_loader, он автоматически ищет user_id, а если не найден, очищает свою session переменную? - person IMCoins; 06.12.2017
comment
Кажется, вы что-то не понимаете ... или нет? Вы используете @login_manager.user_loader для украшения функции. Эта функция принимает id в качестве параметра и возвращает объект User. И если возвращенный объект None, flask-login будет считать этого пользователя недействительным и удалит этого пользователя из session. Чтобы быть более точным, flask-login получить идентификатор пользователя из файла cookie и использовать этот идентификатор для получения соответствующего объекта User. Второй прогресс выполняется login_manager.user_loader. - person Sraw; 06.12.2017
comment
Я немного запутался в вашем издании. Вам не нужно вручную управлять сеансом, просто верните None и позвольте flask-login автоматически выйти из системы текущего пользователя. Не могли бы вы сначала попробовать это решение? Вы можете проверить, None if object is None, это более питонический способ, не используйте if object == None. - person Sraw; 06.12.2017
comment
Спасибо за is None замечание. Все еще изучаю питон. Но две мои проблемы сохраняются. Я хочу отправить пользователю сообщение, когда он / она один раз автоматически выходит из системы. Кроме того, я не понимаю, почему user_loader продолжает вызываться с (истекшим) session_id, когда пользователь вышел из системы. Если user_loader возвращает None для данного идентификатора, по какой причине он продолжает вызывать user_loader с этим идентификатором. Я не хочу вызывать базу данных в другой раз, и я также не думаю, что решение для кеширования на уровне базы данных является хорошим решением. - person doekman; 06.12.2017
comment
Спасибо за обновления. Однако проблема в том, что он будет мигать. Вы автоматически выходили из системы при каждом запросе ... - person doekman; 07.12.2017
comment
@doekman user_loader должен вызываться при каждом запросе браузера к вашему серверу, потому что каждый запрос обрабатывается независимо. Вот как работают HTTP и REST: medium.freecodecamp .org / - person Alan Hamlett; 21.02.2018