Flask应用密码重置提交后跳转404问题求助
Flask密码重置提交后跳转404问题排查
我在Flask Web应用中实现了邮件重置密码功能,目前大部分流程正常:邮件可发送带token的链接,点击能进入新密码输入页面,但提交新密码后会跳转到自定义的404错误页面。怀疑问题出在reset_token函数中,该函数本应将更新后的密码提交至数据库并跳转至登录页,但未正常执行。
相关代码
邮件发送与路由逻辑
def send_reset_email(user): token = user.get_reset_token().decode('utf-8') # Decode bytes to string print("Reset Token:", token) msg = Message('Password Reset Request', sender='noreply@demo.com', recipients=[user.email]) msg.body = f''' To reset your password, visit the following link: {url_for('reset_token', token=token, _external=True)} If you did not make this request, please ignore this email and consider changing your email and password. ''' mail.send(msg) @app.route("/reset_password", methods=['GET', 'POST']) def reset_request(): if current_user.is_authenticated: return redirect(url_for('index')) form = RequestResetForm() if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() send_reset_email(user) flash('Reset password email has been send', 'info') return redirect(url_for('signin')) return render_template('reset_request.html', title='Reset Password', form=form) @app.route("/reset_password/<token>", methods=['GET', 'POST']) def reset_token(token): print( url_for('signin') ) print("Reset Token Route Triggered") if current_user.is_authenticated: print('redirecting you to index') return redirect(url_for('index')) user = User.verify_reset_token(token) if user is None: print('The user value was none') flash('That is an invalid or expired token', 'warning') return redirect(url_for('reset_request')) form = ResetPasswordForm() if form.validate_on_submit(): hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8') user.password = hashed_password print(f"new password is {user.password}") db.session.commit() print("Password has been committed") flash(f'Your password has been updated', 'success') print("Redirecting you now to sign in") return redirect(url_for('signin')) return render_template('reset_token.html', title='Reset Password', form=form)
User模型
class User(db.Model, UserMixin): # Manual Table name choice __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) firstname = db.Column(db.String(100), nullable=False) lastname = db.Column(db.String(100), nullable=False) email = db.Column(db.String(100), unique=True, nullable=False, index=True) password = db.Column(db.String(130), nullable=False) def get_reset_token(self, expires_sec=1800): s = Serializer(app.config['SECRET_KEY']) return s.dumps({'user_id': self.id}).encode('utf-8') @staticmethod def verify_reset_token(token): s = Serializer(app.config['SECRET_KEY']) try: user_id = s.loads(token)['user_id'] except: return None return User.query.get(user_id) def __init__(self,firstname,lastname,email,password): self.firstname = firstname self.lastname = lastname self.email = email self.password = password
表单定义
class RequestResetForm(FlaskForm): email = StringField('Email', validators=[InputRequired(), Email()]) submit = SubmitField('Request Password Reset') def validate_email(self, email): user = User.query.filter_by(email=email.data).first() if user is None: raise ValidationError("There is no account with this email.") class ResetPasswordForm(FlaskForm): password = PasswordField('Password', validators=[InputRequired(), Length(min=4, max=200)]) confirm_password = PasswordField('Confirm Password', validators=[InputRequired(), Length(min=2, max=200), EqualTo('password')]) submit = SubmitField('Reset Password')
排查方向与解决方案
检查模板表单配置
- 确认
reset_token.html模板中包含CSRF令牌:添加{{ form.hidden_tag() }},否则表单验证会失败,导致提交后重新渲染页面,若路由匹配异常则触发404。 - 确保表单action属性为空(或指向当前路由),避免提交到错误路径。
- 确认
验证目标路由存在性
- 确认应用中存在名为
signin的路由函数,检查@app.route装饰器的路径和函数名是否匹配url_for('signin')的调用。
- 确认应用中存在名为
调试表单验证失败原因
- 在
reset_token函数的form.validate_on_submit()判断前添加print(form.errors),查看具体验证错误(如密码长度不匹配、CSRF失效等)。注意表单中password的最小长度是4,而confirm_password是2,两者规则不一致,可能导致验证失败。
- 在
检查Token编码与传输
- 替换
Serializer为URLSafeSerializer,避免token中的特殊字符(如+、/)在URL传输中被转义,导致验证失败:from itsdangerous import URLSafeSerializer def get_reset_token(self, expires_sec=1800): s = URLSafeSerializer(app.config['SECRET_KEY']) return s.dumps({'user_id': self.id})
- 替换
添加数据库异常处理
- 在
db.session.commit()处添加异常捕获,避免数据库错误导致流程中断:try: db.session.commit() print("Password has been committed") except Exception as e: db.session.rollback() print(f"Database error: {e}") flash('Failed to update password, please try again', 'danger')
- 在
内容的提问来源于stack exchange,提问作者Naeim Salib
相关产品推荐
相关产品推荐

