簡體   English   中英

Flask-Login 問題:在身份驗證和重定向后 current_user 仍然是 AnonymousUserMixin 類型

[英]Flask-Login Issue: current_user is still of type AnonymousUserMixin after Authentication and Redirect

我有一個使用自定義 FlaskForm(使用 WTForms)的登錄頁面。 如果用戶輸入正確的憑據,則成功查詢 PostgreSQL 數據庫(使用 flask-sqlalchemy)以查看是否存在具有該名稱和(散列)密碼的用戶。 如果有這樣的用戶,則會運行 login_user(user),並嘗試重定向到我的網站主頁。

我已經實現了燒瓶登錄(根據在線文檔),但是當用戶提供有效的登錄憑據時,他們會被重定向回登錄頁面(就好像他們沒有提供有效的憑據一樣)。 我正在使用谷歌瀏覽器。

我已經確定在重定向到主頁后,current_user 的類型是 AnonymousUserMixin(即使登錄 function 中的當前用戶是 User 類型(我已經定義,繼承了 UserMixin 的所有方法)。

這是我嘗試過的:

  • 確保我的代碼符合 Flask 文檔中列出的規范

  • 瀏覽 StackOverflow、Reddit 和各種博客上的文章。 根據這些,我對我的代碼進行了以下更改:

  • 將 hidden_tag() 和 csrf_token() 字段插入到我的登錄表單中(請參閱最終代碼摘錄)

  • 向我的 Flask 應用程序添加了密鑰

  • 編碼和解碼(使用 utf8)當前用戶的 id(參見下面的代碼,也在下面的用戶 class 定義中)

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

根據燒瓶登錄文檔,我已將以下內容放入我的文件 application.py (我的 flask 代碼所在的文件):

在文件的頂部:

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

一個用戶加載器function:

@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)

一個用戶 class(繼承了 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)

根據燒瓶登錄文檔,我使用 login_user function(見上文)登錄用戶,並檢查下一個 url(我的主頁 - “索引”)是否安全。 如果是,我繼續將用戶重定向到該頁面。

另外,下面是我的登錄表單(其中包括 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

用戶提交表單並嘗試重定向到主頁時的命令行 Output(索引)

127.0.0.1 - - [15/Jun/2020 18:42:35] “GET /login HTTP/1.1”200 -

127.0.0.1 - - [15/Jun/2020 18:42:48] “POST /login HTTP/1.1”302 -

127.0.0.1 - - [15/Jun/2020 18:42:48] “GET / HTTP/1.1”302 -

127.0.0.1 - - [15/Jun/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

預先感謝您的幫助!

更新

以下是我更新的代碼(根據其他人的有見地的評論進行了更改):

登錄 Function:

@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

從我的用戶 class 獲取 ID function:

    def get_id(self):
        return self.id

上述兩個 function 工作正常,但用戶在嘗試使用有效憑據登錄后仍被重定向到登錄頁面。

再次感謝大家的幫助; 非常感謝。

我和你有同樣的問題,當我沒有設置remember=True時,使用flask.login_user后我無法重定向

每個燒瓶登錄文檔: https://flask-login.readthedocs.io/en/latest/#flask_login.login_user remember (bool) – 是否在 session 過期后記住用戶。 默認為假。

所以我想我有一些配置,我的 session 在我的初始請求后立即過期,因為如果我將記住設置為True

所以不要這樣做:

login_user(user)

嘗試

login_user(user=user, remember=True)

建議二:

我的猜測是仔細查看您的 function 以從數據庫中獲取用戶def get(self, user_id): ,確保這是正確返回用戶 object 而不是 None。 如果登錄成功,來自 flask.login 的 login_user() 也應該返回 True。 這就是我如何使用 id 從數據庫中查找和獲取用戶的方式:

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)

您沒有正確實施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 model

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-it-works

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM