Flask -Проблема входа: current_user по-прежнему имеет тип AnonymousUserMixin после аутентификации и перенаправления - PullRequest
1 голос
/ 18 июня 2020

У меня есть страница входа в систему, на которой используется настраиваемая FlaskForm (с использованием WTForms). Если пользователь вводит правильные учетные данные, база данных PostgreSQL успешно опрашивается (с использованием flask -sqlalchemy), чтобы узнать, существует ли пользователь с таким именем и (хешированным) паролем. Если такой пользователь есть, запускается login_user (user) и предпринимается попытка перенаправления на домашнюю страницу моего сайта.

Я реализовал flask -login (согласно онлайн-документации), но когда Пользователь предоставляет действительные учетные данные для входа в систему, они перенаправляются обратно на страницу входа (как если бы они не предоставили действительные учетные данные). Я использую Google Chrome.

Я определил, что после перенаправления на домашнюю страницу current_user имеет тип AnonymousUserMixin (даже если текущий пользователь в функции входа имеет тип User (который я определил, наследуя все методы из UserMixin).

Вот что я пробовал:

  • Убедитесь, что мой код соответствует спецификациям, изложенным в документации Flask

  • Просмотренные статьи в StackOverflow, Reddit и различных блогах. Из них я внес следующие изменения в свой код:

  • Вставил hidden_tag () и csrf_token () в мою форму входа в систему (см. заключительный отрывок кода)

  • Добавлен секретный ключ в мое Flask приложение

  • Закодировано и декодировано ( с utf8) идентификатор текущего пользователя (см. код ниже, также в определении класса User ниже)

    return str (self.id) .encode ('utf-8'). decode ('utf -8 ')

Согласно flask - документация по входу , я поместил в свой файл application.py (файл, в котором находится мой код flask) следующее:

В верхней части файла:

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

Функция загрузчика пользователя:

@login_manager.user_loader
def load_user(id):
    id = db.execute("SELECT id FROM users WHERE id=:id", {"id": id})
    return User.get(current_user, id)

Класс пользователя (наследующий UserMixin):

class User(UserMixin):
    is_active = True
    is_anonymous = False
    def __init__(self, email, name, id, input_password_hash):
        self.id = id
        self.name = name
        self.email = email
        self.password_hash = input_password_hash

    def check_password(self, password, password_hash_byte_literal):
        return bcrypt.checkpw(password.encode('utf8'), password_hash_byte_literal)

    def get_id(self):
        return str(self.id).encode('utf-8').decode('utf-8')

    def get(self, user_id):
        id = db.execute("SELECT id FROM users WHERE id=:user_id", {"user_id": user_id})
        if id:
            name = db.execute("SELECT name FROM users WHERE id=:user_id", {"user_id": user_id})
            email = db.execute("SELECT email FROM users WHERE id=:user_id", {"user_id": user_id})
            password_hash = db.execute("SELECT password_hash FROM users WHERE id=:user_id", {"user_id": user_id})
            user_name_string = ''
            user_email_string = ''
            user_password_hash_string = ''
            for row in name:
                for i in range(len(row)):
                    user_name_string += row[i]
            for row in email:
                for i in range(len(row)):
                    user_email_string += row[i]
            for row in password_hash:
                for i in range(len(row)):
                    user_password_hash_string += row[i]
            return User(user_email_string, user_name_string, user_id, user_password_hash_string)
        else:
            return None

Ниже мой маршрут входа:

@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        email = form.email.data
        password = form.password.data
        user_pw_hash = (db.execute("SELECT password_hash FROM users WHERE email=:email", {"email": email}).fetchone())
        user_id = (db.execute("SELECT id FROM users WHERE email=:email", {"email": email}).fetchone())
        if user_id:
            password_hash_string = ''
            id_string = str(user_id)
            for row in user_pw_hash:
                for i in range(len(row)):
                    password_hash_string += row[i]
            user_id_int = int(id_string[1])
            user = User.get(user, user_id_int)
            password_hash_byte_literal = bytes(password_hash_string, encoding='utf8')
            correct_password = User.check_password(user, password, password_hash_byte_literal)
            if correct_password:
                login_user(user)
                next = url_for("index")
                if not is_safe_url(next, {"http://127.0.0.1:5000"}):
                    return abort(400)
                return redirect(next or url_for("login"))
            else:
                return render_template("login.html", message="Incorrect username or password.", form=form)
        else:
            return render_template("login.html", message="No account with that email address was found.", form=form)

    else:
        return render_template("login.html", form=form)

Согласно документации flask -login, я вхожу в систему с помощью функции login_user (см. Выше) и проверяю, безопасен ли следующий URL (моя домашняя страница - «index»). Если это так, я перехожу к перенаправлению пользователя на эту страницу.

Также ниже представлена ​​моя форма входа (которая включает поля hidden_tag () и csrf_token ()).

<form method="post" action="/login">
    {{ form.hidden_tag() }}
    {{ form.csrf_token() }}
    {{ wtf.form_field(form.email) }}
    {{ wtf.form_field(form.password) }}
    <button type="submit" value="submit">Submit</button><br>
</form>

Я понимаю, что этот код еще не очищает входные данные должным образом перед выполнением команд PostgreSQL. Я постараюсь исправить эту проблему в ближайшее время.

Импорт:

import os
from flask import flash, Flask, session, redirect, render_template, request, url_for
from flask_bootstrap import Bootstrap
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_session import Session
from is_safe_url import is_safe_url
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from forms import LoginForm, RegistrationForm, ReviewForm   # Custom WTForms I wrote
import bcrypt

Попытка вывода командной строки, когда пользователь отправляет форму и перенаправляется на домашнюю страницу (индекс)

127.0.0.1 - - [15 / июн / 2020 18:42:35] «GET / login HTTP / 1.1» 200 -

127.0.0.1 - - [15 / июн / 2020 18 : 42: 48] «POST / вход HTTP / 1.1» 302 -

127.0.0.1 - - [15 / июн / 2020 18:42:48] «GET / HTTP / 1.1» 302 -

127.0.0.1 - - [15 / июн / 2020 18:42:48] «GET / login? Next =% 2F HTTP / 1.1» 200 -

Я использую код Visual Studio (и его PowerShell), чтобы запустить и отредактировать это приложение Flask.

Версии:

Windows 10
Google Chrome Version 83.0.4103.106 (Official Build) (64-bit)
bcrypt 3.1.7
email-validator 1.1.1
Python 3.8.2
Flask 1.1.2
Flask-WTF 0.14.3
Flask-SQLAlchemy 2.4.3
Flask-Session 0.3.2
Flask-Login 0.5.0
Flask-Bootstrap
WTForms 2.3.1
SQLAlchemy 1.3.16
mysql-connector-python 8.0.19
mysql-client 0.0.1
Jinja2 2.11.2
itsdangerous 1.1.0
is-safe-url 1.0

Заранее благодарим вас за помощь!

Обновление

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

Функция входа в систему:

@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        email = form.email.data
        password = form.password.data
        user_id = (db.execute("SELECT id FROM users WHERE email=:email", {"email": email}).fetchone())
        if user_id:
            user_pw_hash = (db.execute("SELECT password_hash FROM users WHERE email=:email", {"email": email}).fetchone())
            password_hash_string = user_pw_hash.password_hash
            user = User(None, None, None, False)
            user_id_int = user_id.id
            user = load_user(user_id_int)
            password_hash_byte_literal = bytes(password_hash_string, encoding='utf8')
            correct_password = User.check_password(user, password, password_hash_byte_literal)
            if correct_password:
                login_user(user)
                next = url_for("index")
                if not is_safe_url(next, {"http://127.0.0.1:5000"}):
                    return abort(400)
                else:
                    return redirect(next or url_for("login"))
            else:
                return render_template("login.html", message="Incorrect email or password.", form=form)
        else:
            return render_template("login.html", message="No account with that email address was found.", form=form)
    else:
        return render_template("login.html", form=form)

Пользовательский загрузчик диспетчера входа:

@login_manager.user_loader
def load_user(id):
    user_data = db.execute("SELECT * FROM users WHERE id=:id", {"id": id}).fetchone()
    if user_data:
        return User(user_data.email, user_data.name, id, user_data.password_hash)
    else:
        return None

A Получить функцию идентификатора из моего класса User:

    def get_id(self):
        return self.id

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

Еще раз спасибо всем за вашу помощь; это очень ценно.

Ответы [ 2 ]

0 голосов
/ 08 июля 2020

Вы неправильно выполняете требования flask-login. Попробуйте использовать обратный вызов по умолчанию user_loader и посмотрите, решит ли он вашу проблему.

@login_manager.user_loader
def load_user(id):
    # Whichever method you use to load a user, it needs to be guaranteed unique
    field_values = list(db.execute("SELECT id, name, email, password_hash FROM users WHERE id=:id", {"id": id}))
    
    return User(**dict(field_values))

и в вашей модели User

def get_id(self):
    # this matches what user_loader needs to uniquely load a user
    return self.id

См.: https://flask-login.readthedocs.io/en/latest/#how - это работает

0 голосов
/ 23 июня 2020

У меня та же проблема, что и у вас, когда я не устанавливаю Remember = True, я не могу перенаправить после использования flask .login_user

Per flask -login docs: https://flask-login.readthedocs.io/en/latest/#flask_login .login_user Remember (bool) - следует ли помнить пользователя после истечения его сеанса. По умолчанию False.

, поэтому я думаю, что у меня есть некоторая конфигурация, при которой мой сеанс истекает сразу после моего первоначального запроса, потому что, если я установлю, запомните True

поэтому вместо того, чтобы делать:

login_user(user)

попробуйте

login_user(user=user, remember=True)

Предложение 2:

я предполагаю, что нужно дважды взглянуть на вашу функцию, чтобы получить пользователь из базы данных def get (self, user_id): , убедитесь, что это возвращает объект пользователя правильно, а не None. Также login_user () из flask .login должен вернуть True, если вход в систему прошел успешно. Вот как я могу найти и получить пользователя из базы данных с помощью идентификатора:

def find_user_by_id(user_id: int):
row = db.execute('select * from users u where u.id =:user_id',
                 {'user_id': user_id}).fetchone()
if row is None:
    return None
else:
    return User(user_id=row.id, first_name=row.first_name,
                last_name=row.last_name, email=row.email, reviews=None, password_hash=row.password)
...