python - Flask-Login 问题:在身份验证和重定向后 current_user 仍然是 AnonymousUserMixin 类型
问题描述
我有一个使用自定义 FlaskForm(使用 WTForms)的登录页面。如果用户输入正确的凭据,则会成功查询 PostgreSQL 数据库(使用 flask-sqlalchemy)以查看是否存在具有该名称和(散列)密码的用户。如果有这样的用户,则会运行 login_user(user),并尝试重定向到我的网站主页。
我已经实现了烧瓶登录(根据在线文档),但是当用户提供有效的登录凭据时,他们会被重定向回登录页面(就好像他们没有提供有效的凭据一样)。我正在使用谷歌浏览器。
我已经确定在重定向到主页后,current_user 的类型是 AnonymousUserMixin(即使登录函数中的当前用户是 User 类型(我已经定义,继承了 UserMixin 的所有方法)。
这是我尝试过的:
确保我的代码符合 Flask 文档中列出的规范
浏览 StackOverflow、Reddit 和各种博客上的文章。根据这些,我对我的代码进行了以下更改:
将 hidden_tag() 和 csrf_token() 字段插入到我的登录表单中(请参阅最终代码摘录)
向我的 Flask 应用程序添加了一个密钥
编码和解码(使用 utf8)当前用户的 id(见下面的代码,也在下面的 User 类定义中)
返回 str(self.id).encode('utf-8').decode('utf-8')
根据烧瓶登录文档,我已将以下内容放入我的文件 application.py (我的烧瓶代码所在的文件):
在文件的顶部:
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)
根据烧瓶登录文档,我使用 login_user 函数(见上文)登录用户,并检查下一个 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
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
预先感谢您的帮助!
更新
以下是我更新的代码(根据其他人的有见地的评论进行了更改):
登录功能:
@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
我的 User 类中的 Get ID 函数:
def get_id(self):
return self.id
上述两个功能正常工作,但用户在尝试使用有效凭据登录后仍被重定向到登录页面。
再次感谢大家的帮助;非常感谢。
解决方案
您没有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-it-works
推荐阅读
- ios - iOS tool to measure battery drain
- python - 通过python列表中其值的子字符串获取元素
- opencv - 缺少 OpenCV 透明 API UMat
- dns - 使用一个 IP 地址指向另一个可变 IP 地址
- javascript - Creativ Tim 的 Vuetify 模板无法正常工作
- javascript - jQuery:避免分配不同的 id 来切换重复的元素
- php - 为什么我对带有文本字段的大型表的 MySQL 查询非常慢,尽管索引和查询时间应该很短?
- uml - UML 类图:操作与关联
- java - Android Studio:当应用程序在前台时,无法在 sdk < 26 上接收推送消息
- python - 在 Python 中删除括号、引号和 u 的 CSV 文件