Flask Login - @login_manager.token_loader не вызывается

Я пытаюсь использовать токен для аутентификации пользователей.

Мне удалось заставить процесс работать, используя:

@login_manager.user_loader
def load_user(userid):
    from models import User
    return User.get(userid)

Однако я не знаю, как заставить процесс работать при использовании token.

В моем файле views.py у меня есть:

from flask import request, jsonify, url_for, render_template, session

@login_manager.token_loader
def load_token(token):

    # Check token and return user

Когда я вхожу в систему пользователя:

def _do_login_user(user):
    # User must have been authenticated
    if not user: raise_error('user does not exist', 'A user with this email address does not exist', 404)     

    # Check if user account is enabled
    if not user.is_active:
        raise_error('inactive account', 'Your account has not been enabled. Check your email Inbox and Spam folders for the confirmation email', 401)

    # Use Flask-Login to sign in user
    login_user(user)

    return json.dumps({"data":{"success":"true", "message":"You have logged in successfully!"}})

Модель пользователя

class User(db.Model, UserMixin):

    def get_auth_token(self):
        s = Serializer(app.config['SECRET_KEY'], expires_in = 6000)
        return s.dumps({'id': self.id, 'password': self.password})

В какой момент я должен вызывать User.get_auth_token() и должен ли я вручную устанавливать этот токен в session?

Наконец, когда @login_manager.token_loader вызывают?

Спасибо за вашу помощь.


person Community    schedule 01.02.2016    source источник


Ответы (2)


`@login_manager.token_loader` полностью посвящен функции «запомнить меня».

Он будет вызываться только тогда, когда браузер посещает ваш сайт без файла cookie сеанса, а пользователь ранее вошел в систему с помощью login_user(user, remember=True).

Вероятно, вы можете стимулировать flask-login для вызова вашей декорированной функции token_loader следующим образом:

  • вход в систему с «запомнить меня»
  • закройте браузер
  • снова откройте браузер и перейдите на страницу «login_required»

... или используйте что-то вроде кода в конце этого ответа.

Вам не нужно звонить User.get_auth_token() самостоятельно. Flask-login вызовет его, когда вы вызовете login_user(user, remeber=True) и определите @login_manager.token_loader

Взгляните на LoginManager._set_cookie и LoginManager._load_from_cookie, чтобы увидеть, как Flask-Login делает все это.

Ваша функция _do_login_user вызывает login_user без remember=True, поэтому ни одна из функций «запомнить меня» не вызывается.

Ниже приведен код, который должен иллюстрировать, когда вызываются рассматриваемые функции:

from flask import Flask
from flask.ext.login import LoginManager, UserMixin, login_user, login_required

app = Flask('foo')
app.config['SECRET_KEY'] = 'abcd'
login_manager = LoginManager(app)


class User(UserMixin):
    def get_id(self):
        return 1

    def get_auth_token(self):
        print("get_auth_token called")
        return 'my_auth_token'


@login_manager.token_loader
def my_token_loader(token):
    print("my_token_loader called")
    return User()


@app.route('/login')
def login():
    login_user(User(), remember=True)
    return ''


@app.route('/protected')
@login_required
def protected():
    return ''


with app.test_client() as tc:
    # login will set session cookie as well as remember me cookie
    # User.get_auth_token will be called because we have defined a token_loader
    tc.get('/login')

    # clear the session cookie so the next request will use remember me
    tc.cookie_jar.clear_session_cookies()

    # our token_loader function should be called now as Flask-Login attempts to
    # log us in with the remember me cookie
    tc.get('/protected')
person Jeremy Allen    schedule 09.02.2016
comment
Спасибо, Джереми. Я сделал, как вы сказали, и, похоже, это работает до определенной степени. Проблема, с которой я сейчас сталкиваюсь, заключается в том, что если я Login, Access Protected Page, а затем Access Protected Page во второй раз сразу после того, как сделал это в первый раз, я получаю internal server error, говорящий, что мне нужно реализовать user_loader. Я также заметил, что снова была установлена ​​переменная session. Знаете ли вы, почему это происходит? - person ; 10.02.2016
comment
@Giri То, что вы описываете, является ожидаемым поведением. Да, вам нужно реализовать user_loader и User.get_id в вашем приложении. token_loader касается только функции «запомнить меня». Вы определяете его, когда хотите повысить безопасность своих запоминающих файлов cookie. Flask-Login использует token_loader для создания обычного сеанса, который содержит идентификатор, сгенерированный User.get_id и отправленный user_loader при последующих загрузках страницы. - person Jeremy Allen; 10.02.2016
comment
Ах понял. Спасибо, Джереми! - person ; 10.02.2016
comment
Я не нахожу login_manager.token_loader в flask-login, должно быть login_manager.request_loader? - person nos; 03.07.2017

У меня было плохое время, пытаясь заставить это работать, а затем я отказался от использования обычного user_loader, единственное, что я могу предложить, это посмотреть здесь flask-login и проверьте фактический код, из которого вы видите, что он добавляет user_id в сеанс, поэтому вам не нужно заботиться о ней.

В любом случае, я потратил довольно много времени, пытаясь заставить его работать, и в конце концов я выбрал что-то похожее на это jwt и flask-логин

@login_manager.user_loader
def load_user(token):
    try:
        id = jwt.decode(token, current_app.config.get(
            'KEY', None))['_id']
        return user_functions.get_user_by_id(id, token)
    except (ServiceUnavailable, ServiceResponseError) as e:
        current_app.logger.exception(e.message)
        return None



class Login(MethodView):

    methods = ['GET', 'POST']

    def post(self):
        form = LoginForm(request.form)
        registered_user = form.validate()
        if not registered_user:
            return render_template('pages/login.html', login_form=form), 403
        login_user(registered_user, remember=False)
        return redirect(request.args.get('next') or url_for('public.index'))

не уверен, насколько это полезно, но когда у вас кончается терпение, стоит прочитать

person shipperizer    schedule 03.02.2016