-1

我正在尝试通过向 Gmail 帐户发送电子邮件来添加重置用户密码的功能。我一直在关注CoreySchafer YouTube 教程Miguel Grinberg 教程来实现这一点。

总体思路是用户将收到密码重置请求表的提示,他们将在其中输入他们想要重置密码的电子邮件地址。单击“请求重置密码”后,将显示一条消息,表明电子邮件已发送到他们的 Gmail 帐户。通过单击电子邮件中的链接,用户将能够重置其密码。

相关文件名的代码如下:

文件:路由.py

# Reset Password Request Route
@app.route("/reset_password", methods = ["GET", "POST"])
def reset_password_request():

    if current_user.is_authenticated:
        return redirect(url_for("dashboard"))

    form = ResetPasswordRequestForm()

    if form.validate_on_submit():
        user = User.query.filter_by(email = form.email.data).first()
        if user:
            send_password_reset_email(user)
            flash("Please check your email for the instructions to reset your password")
            return redirect(url_for("login"))

    return render_template("reset_password_request.html", title = "Request For Reset Password", form = form)

# Password Reset Route
@app.route("/reset_password/<token>", methods = ["GET", "POST"])
def reset_password(token):
    if current_user.is_authenticated:
        return redirect(url_for("dashboard"))

    user = User.verify_reset_password_token(token)

    if not user:
        flash("Invalid Token")
        return redirect(url_for("reset_password_request"))

    form = ResetPasswordForm()

    if form.validate_on_submit():
        user.set_password(form.password.data)
        db.session.commit()
        flash("Congratulations! Your password has been reset successfully.")
        return redirect(url_for("login"))

    return render_template("reset_password.html", title = "Reset Password", form = form) 

文件:forms.py

# Reset Password Request Form
class ResetPasswordRequestForm(FlaskForm):
    email = StringField("Email", validators = [DataRequired(message = "Email Address is required."), Email()], render_kw = {"placeholder": "Email Address"})
    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 that email. You must register first.")

# Password Reset Form
class ResetPasswordForm(FlaskForm):
    password = PasswordField("Password", validators = [DataRequired(message = "Password is required.")], render_kw = {"placeholder": "Password"})
    confirm_password = PasswordField("Repeat Password", validators = [DataRequired(message = "Password Confirmation is required."), EqualTo("password")], render_kw = {"placeholder": "Confirm Password"})
    submit = SubmitField("Reset Password")

文件:email.py

from flask import render_template
from flask_mail import Message
from app import app, mail
from threading import Thread

# Sending Emails Asynchronously
def send_async_email(app, msg):
    with app.app_context():
        mail.send(msg)

# Email Sending Wrapper Function
def send_email(subject, sender, recipients, text_body, html_body):
    msg = Message(subject, sender = sender, recipients = recipients)
    msg.body = text_body
    msg.html = html_body
    Thread(target = send_async_email, args = (app, msg)).start()

# Send Password Reset Email Function
def send_password_reset_email(user):
    token = user.get_reset_password_token()

    send_email("【Task Manager】Reset Your Password", 
    sender = app.config["ADMINS"][0], 
    recipients = [user.email], 
    text_body = render_template("email/reset_password.txt", user = user, token = token), 
    html_body = render_template("email/reset_password.html", user = user, token = token))

文件:models.py

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

# Reset Password Support in User Model
def get_reset_password_token(self, expires_sec = 1800):
    s = Serializer(app.config["SECRET_KEY"], expires_sec)
    return s.dumps({"user_id": self.id}).decode("utf-8")

# Verifying Reset Password Token in User Model
@staticmethod
def verify_reset_password_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)

文件:reset_password_request.html

{% extends "layout.html" %}

{% block content %}
    <h1>Task Manager</h1>
    <form action="" method="POST">
        {{ form.hidden_tag() }}
        <div>
            {{ form.email(size=64) }}
        </div>
        {% for error in form.email.errors %}
            <span style="color: red;">{{ error }}</span>
        {% endfor %}
        <div>
            {{ form.submit() }}
        </div>
    </form>
{% endblock %}

文件:reset_password.html

{% extends "layout.html" %}

{% block content %}
    <h1>Task Manager</h1>
    <form action="" method="POST" novalidate>
        {{ form.hidden_tag() }}
        <div>
            {{ form.password(size=32) }}
        </div>
        {% for error in form.password.errors %}
            <span style="color: red;">{{ error }}</span>
        {% endfor %}
        <div>
            {{ form.confirm_password(size=32) }}
        </div>
        {% for error in form.confirm_password.errors %}
            <span style="color: red;">{{ error }}</span>
        {% endfor %}
        <div>
            {{ form.submit() }}
        </div>
    </form>
{% endblock %}

我已将环境变量保存在根目录的 .env 文件中。

SECRET_KEY="simple-is-better-than-complex"
MAIL_SERVER="smtp.googlemail.com"
MAIL_PORT=587
MAIL_USE_TLS=True
MAIL_USERNAME="jeet.java.13"
MAIL_PASSWORD="pass123"

还在项目根目录中创建了 config.py 文件。

from dotenv import load_dotenv

load_dotenv(override=True)

import os

basedir = os.path.abspath(os.path.dirname(__file__))

# Application Configurations
class Config(object):

    # Function: Getting Environment Variables
    def get_env_var(name):
        try:
            return os.environ[name]
        except KeyError:
            message = "Expected Environment Variable '{}' Not Set!".format(name)
            raise Exception(message)

    # SECRET_KEY Configuration
    SECRET_KEY = os.getenv("SECRET_KEY")
    SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(basedir, "tms.db")

    SQLALCHEMY_TRACK_MODIFICATIONS = False

    # EMAIL CONFIGURATION

    MAIL_SERVER = os.getenv("MAIL_SERVER")
    MAIL_PORT = int(os.getenv("MAIL_PORT")) or 25
    MAIL_USE_TLS = os.getenv("MAIL_USE_TLS")
    MAIL_USERNAME = os.getenv("MAIL_USERNAME")
    MAIL_PASSWORD = os.getenv("MAIL_PASSWORD")
    ADMINS = ["jeet.java.13@gmail.com"]

终端结果:

"POST /reset_password HTTP/1.1" 302 -

我还为我的 Gmail 帐户打开了“安全性较低的应用程序”,但仍然无法发送电子邮件。在执行 Flask 应用程序期间,终端中没有错误。

期待您最亲切的支持。

4

3 回答 3

1

Google 会阻止不安全的应用对您的帐户进行身份验证。您需要使用Google 帐户中安全性较低的应用管理器手动启用对您帐户的访问权限。浏览此链接并将允许安全性较低的应用程序:关闭状态设置为允许安全性较低的应用程序:如果使用拨动开关将其禁用,则打开:

不太安全的应用访问管理器

于 2020-12-27T11:25:21.777 回答
0

我在以前的应用程序中遇到了类似的问题,正在配置烧瓶邮件。我相信它 MAIL_SUPPRESS_SEND 什么的。在这里检查Flask-Mail 不发送电子邮件,没有报告错误

于 2020-05-12T16:48:10.303 回答
0

问题解决了。在定义环境变量时,MAIL_USERNAME 需要是完整的电子邮件(Gmail)地址;不仅是 Gmail ID。

于 2020-05-14T10:54:28.160 回答