Flask项目中user_db.hashed_password查询返回None的原因排查
问题描述
在Flask项目中,查询数据库得到user_db变量后,调用flash(user_db.username)和flash(user_db.email)可正常输出对应值,但调用flash(user_db.hashed_password)时返回None。相关代码如下:
from flask import Flask app = Flask(__name__) class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True) hashed_password = db.Column(db.String(128)) email = db.Column(db.String(120), unique=True) from argon2 import PasswordHasher def hash_password(self, plaintext_password_form): ph = PasswordHasher() self.password_hashed_form = ph.hash(plaintext_password_form) return self.password_hashed_form def compare_hashed_passwords(self, hashed_password_db, plaintext_password_form): ''' The code runs in the /login route. You should query the db for hashed_password or the value will be a different hash; and the function ph.verify will return False even if the password form match with the hash password. ''' ph = PasswordHasher() try: ph.verify(hashed_password_db, plaintext_password_form) signedin = True except: signedin = False return signedin # have to redirect @app.route("/home", methods = ['POST', 'GET']) def home(): render_template(home.html) class RegistrationForm(FlaskForm): ''' This is in /register route. The forms are username, email, password and confirm_password ''' username = StringField('username') email = StringField('email') password = PasswordField('password') confirm_password = PasswordField('confirm_password') submit = SubmitField('Submit'), @app.route("/register", methods = ['POST', 'GET']) def register(): # if the user is logged in make so they can't go to the register page. if current_user.is_authenticated: return redirect(url_for(('auth.home'))) form = RegistrationForm() # form.validate_on_submit(): are always the same line of render template to always allow a get request. if form.validate_on_submit(): username_form = form.username.data email_form = form.email.data plaintext_password_form = form.password.data confirm_plaintext_password_form = form.confirm_password.data # Use the code, if you are adding code to the database the first time adding_user = User(username=username_form, email=email_form) # hash the password hashed_password_form = adding_user.hash_password(plaintext_password_form) adding_user_hahed_password = User(hashed_password=hashed_password_form) db.session.add(adding_user, adding_user_hahed_password ) db.session.commit() return redirect(url_for('auth.login')) return render_template('register.html',title='register', form=form) def check_if_username_or_email_is_in_db(form, field): ''' if the username or email is in the db the code works, if not it raises an ValidationError. The if statement checks if the query is empty/has no values in db. This runs in the LoginForm in auth/forms.py ''' # if empty list [] return True if not User.query.filter_by(username=field.data).first() or not User.query.filter_by(username=field.data).first(): raise ValidationError('The username or email does not exist. Please retype your username or email.') # I included this validator because I am not sure if that could be causing the error. class LoginForm(FlaskForm): ''' This is in /Login route. The forms are username, email, password and confirm_password ''' username_or_email = StringField('username_or_email', validators= [ check_if_username_or_email_is_in_db ]) password = PasswordField('password') submit = SubmitField('Submit') @app.route("/login",methods = ['POST', 'GET']) def login(): # if the user is logged in make it so they can't go to the login page. if current_user.is_authenticated: return redirect(url_for('auth.home')) form = LoginForm() if form.validate_on_submit(): # allows you to register with a username or email username_or_email_form = form.username_or_email.data plaintext_password_form = form.password.data username_db = User.query.filter_by(username=username_or_email_form).first() email_db = User.query.filter_by(email=username_or_email_form).first() if username_db.username == username_or_email_form: user_db = User.query.filter_by(username=username_or_email_form ).first() flash(user_db) elif email_db.email == username_or_email_form: user_db = User.query.filter_by(email=username_or_email_form).first() else: flash('username or email do not exist') flash(user_db) flash(user_db.hashed_password) hashed_password_db = user_db.hashed_password return render_template('home.html') if __name__ == '__main__': app.run(debug=True)
问题原因及解决办法
导致hashed_password返回None的核心原因是注册逻辑错误,密码哈希值根本没存入正确的用户记录中,同时User类的密码哈希方法也存在字段不匹配问题,具体问题及修复如下:
1. 注册时创建了两个独立的User对象(最核心问题)
在register路由中,你先创建了包含用户名和邮箱的adding_user,又创建了仅包含哈希密码的adding_user_hahed_password,还将两个对象都添加到数据库。这会生成两条独立的用户记录:一条只有用户名和邮箱,另一条只有哈希密码,所以登录时查到的用户记录自然没有hashed_password值。
修复方式:只创建一个User对象,一次性设置所有字段。
2. User类的hash_password方法赋值字段错误
hash_password方法中给self.password_hashed_form赋值,但数据库字段名是hashed_password,导致哈希密码没赋值到正确的对象属性上,即使逻辑正确也存不进数据库。
修复方式:修改方法内的赋值为self.hashed_password。
3. 其他次要问题(避免后续报错)
home路由缺少return语句,会触发500错误;check_if_username_or_email_is_in_db验证器的判断重复,应同时检查用户名和邮箱;login路由中如果username_db为None,直接访问username_db.username会抛出AttributeError,需先判断对象是否存在。
修正后的核心代码示例
修正后的User类
class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True) hashed_password = db.Column(db.String(128)) email = db.Column(db.String(120), unique=True) from argon2 import PasswordHasher def hash_password(self, plaintext_password_form): ph = PasswordHasher() # 赋值给数据库对应的字段名hashed_password self.hashed_password = ph.hash(plaintext_password_form) return self.hashed_password
修正后的register路由
@app.route("/register", methods = ['POST', 'GET']) def register(): if current_user.is_authenticated: return redirect(url_for('home')) form = RegistrationForm() if form.validate_on_submit(): username_form = form.username.data email_form = form.email.data plaintext_password_form = form.password.data # 只创建一个User对象 adding_user = User(username=username_form, email=email_form) # 直接调用方法给对象的hashed_password赋值 adding_user.hash_password(plaintext_password_form) # 只添加这一个对象到数据库 db.session.add(adding_user) db.session.commit() return redirect(url_for('login')) return render_template('register.html',title='register', form=form)
修正后的login路由(避免None报错)
@app.route("/login",methods = ['POST', 'GET']) def login(): if current_user.is_authenticated: return redirect(url_for('home')) form = LoginForm() if form.validate_on_submit(): username_or_email_form = form.username_or_email.data plaintext_password_form = form.password.data # 一次性查询是否存在用户名或邮箱匹配的用户 user_db = User.query.filter( (User.username == username_or_email_form) | (User.email == username_or_email_form) ).first() if user_db: flash(user_db.username) flash(user_db.email) flash(user_db.hashed_password) # 现在会正常输出哈希密码 # 此处可继续添加密码验证逻辑 else: flash('用户名或邮箱不存在') return render_template('home.html')
内容的提问来源于stack exchange,提问作者jrgop
相关产品推荐
相关产品推荐

