【问题标题】:Email verification with flask-mail使用烧瓶邮件验证电子邮件
【发布时间】:2022-02-01 05:59:57
【问题描述】:

我希望使用 flask-mail 将电子邮件验证添加到我的 web 应用程序中,在阅读文档后,我似乎必须使用以下方法创建一个 Mail 实例:

app = Flask(__name__)
mail = Mail(app)

然后导入应用和邮件实例。

但是,我当前的代码在函数中创建了 Flask 和 Mail 实例,如下所示:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager 

db = SQLAlchemy()

def create_app():
    app = Flask(__name__)

    app.config["SECRET_KEY"] = "9OLWxND4o83j4K4iuopO"
    app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///db.sqlite"

    db.init_app(app)

    login_manager = LoginManager()
    login_manager.login_view = "auth.login"
    login_manager.init_app(app)

    from .models import User

    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(int(user_id))

    from .auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint)

    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    return app

以上代码在我的__init__.py 文件中。 我无法将 Mail 实例导入到我注册用户的其他文件中,因为实际上并没有定义一个,它只是在一个函数中。 基本代码来自本教程:https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login,现在我正在向其中添加电子邮件验证。 要运行 Web 应用程序,我在 Python REPL 中键入 db.create_all(app=create_app()),这会创建我的 sqlite 数据库,并且是唯一一次调用 create_all() 函数。然后我在我的 powershell 终端中输入Flask run

【问题讨论】:

    标签: python email flask


    【解决方案1】:

    解决方案是两阶段初始化,几乎所有Flask扩展都支持:

    from flask import Flask
    from flask_mail import Mail
    
    mail = Mail()
    
    
    def create_app():
        app = Flask(__name__)
        ...
        mail.init_app(app)
        ...
    

    这允许您从另一个模块导入mail

    我最近刚刚实施了电子邮件验证,我遵循了这个古老但仍然大部分有效的教程:

    http://www.patricksoftwareblog.com/confirming-users-email-address/

    【讨论】:

    • 您发送的教程(以及我正在关注的另一个教程)使用以下行:confirm_serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])。如何导入app
    • @Jeevs09 我上面的回答解决了你的问题吗?如果您接受并支持我的回答,我会很高兴。如果不清楚不同之处,请创建一个新问题。当您使用应用程序工厂模式(即使用 create_app)时,您不会导入应用程序,而是执行from flask import current_app 并使用current_app 作为app 的替代品,请参阅flask.palletsprojects.com/en/1.1.x/appcontext
    • 我强烈推荐以下两场讲座,这将帮助您了解 Flask youtube.com/watch?v=xNo-eOfZH5Q&t=1syoutu.be/fq8y-9UHjyk 背后的概念
    【解决方案2】:

    与许多其他扩展一样,安装flask-mail 后,您需要在__init__.py 文件中创建一个mail 对象。使用如下所示的结构,其中使用了蓝图,您可以将电子邮件支持添加到您的烧瓶应用程序。

    project_folder
        | --- app.py
        | --- config.py
        | --- app/
              | --- email.py
              | --- models.py
              | --- __init__.py
              | --- main/
                     | --- __init__.py
                     | --- routes.py
                     | --- email.py
              | --- auth/
                     | --- __init__.py
                     | --- routes.py
              | --- templates/
                     | --- auth/
                            | --- register.html
    

    在应用工厂中创建mail对象:

    # app/__init.py
    
    from flask import Flask
    from flask_mail import Mail
    # ...
    
    mail = Mail()
    # ...
    
    
    def create_app():
        app = Flask(__name__)
        # ...
    
        mail.init_app(app)
        # ...
    

    创建一个电子邮件模块,它将处理您的应用程序的所有电子邮件支持需求,如下所示:

    # app/email.py
    
    from threading import Thread
    from flask import current_app
    from flask_mail import Message
    from app import mail
    
    
    def send_async_email(app, msg):
        with app.app_context():
            mail.send(msg)
    
    
    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=(current_app._get_current_object(), msg)).start()
    

    在上面,我已经导入了我们在__init__.py 文件中创建的mail。由于我们使用的是工厂函数,我从flask 中导入current_app,这将有助于访问应用程序的配置变量。这些变量是完成电子邮件支持所必需的。线程确保在执行电子邮件设置时应用程序不会变慢。

    我假设您想向已注册的用户发送电子邮件。因此,在auth 包中,您需要创建一个帮助方法来将电子邮件发送给用户。

    # app/auth/email.py
    
    from flask import render_template, current_app
    from app.email import send_email
    
    
    def send_congrats_email(user):
        send_email('[Congrats] You are registered'),
                   sender=current_app.config['ADMINS'][0],
                   recipients=[user.email],
                   text_body=render_template('email/reset_password.txt',
                                             user=user),
                   html_body=render_template('email/reset_password.html',
                                             user=user)
    

    在你的auth/routes.py中,创建一个视图函数进行注册:

    # app/auth/routes.py
    
    from app.auth.email import send_congrats_email
    
    @bp.route('/register', methods=['GET', 'POST'])
    def register():
        if current_user.is_authenticated:
            return redirect(url_for('main.index'))
        form = RegistrationForm()
        if form.validate_on_submit():
            user = User(username=form.username.data, email=form.email.data)
            user.set_password(form.password.data)
            db.session.add(user)
            db.session.commit()
            send_congrats_email(user)
            flash('Check your email for our congrats message')
            return redirect(url_for('auth.login'))
        return render_template('auth/register.html', title='Register',
                               form=form)
    

    确保在config 模块中设置您的电子邮件配置:

    import os 
    from dotenv import load_dotenv
    
    basedir = os.path.abspath(os.path.dirname(__file__))
    load_dotenv(basedir, '.env')
    
    
    class Config(object):
        # Database configuration
        SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL?ssl=require') or \
            'sqlite:///' + os.path.join(basedir, 'app.db')
        SQLALCHEMY_TRACK_MODIFICATIONS = False
    
        # Form protection
        SECRET_KEY = os.environ.get('SECRET_KEY')
    
        # Email configuration
        MAIL_SERVER = os.environ.get('MAIL_SERVER')
        MAIL_PORT = int(os.environ.get('MAIL_PORT') or 25)
        MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS') is not None
        MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
        MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
        ADMINS = os.environ.get('ADMINS')
    
    

    这样,当调用register() 视图函数时,您的电子邮件消息(在app/templates/email/ 中的模板中可见)将发送给新注册的用户。

    【讨论】:

      【解决方案3】:

      我最近遇到了同样的问题,我决定通过为(我的)电子邮件库创建一个 Flask 扩展来解决它。这个扩展 (Flask-Redmail) 与 Flask-Mail 非常相似,但它的功能更加丰富,并且依赖于一个经过良好测试且强大的库,称为 Red Mail

      我在这里写了我是如何做到的:https://flask-redmail.readthedocs.io/en/latest/cookbook.html#verification-email

      简而言之,你需要做什么:

      • 获取用户指定的电子邮件(和密码)
      • 将用户作为未经验证的用户存储到您的用户数据库中
      • 向用户发送一封电子邮件,其中包含可识别他/她的唯一 URL
      • 创建此 URL 端点并将用户设置为在访问时进行验证。

      为了实现这些,我建议使用:

      接下来,我将演示如何做到这一点。为您的应用程序创建文件(即app.py):

      from flask import Flask
      from flask_redmail import RedMail¨
      from flask_sqlalchemy import SQLAlchemy
      from flask_login import LoginManager
      
      email = RedMail()
      db = SQLAlchemy()
      login_manager = LoginManager()
      
      def create_app():
          app = Flask(__name__)
          
          # Configure the sender
          app.config["EMAIL_HOST"] = "localhost"
          app.config["EMAIL_PORT"] = 587
          app.config["EMAIL_USER"] = "me@example.com"
          app.config["EMAIL_PASSWORD"] = "<PASSWORD>"
      
          # Set some other relevant configurations
          app.config["SECRET_KEY"] = "GUI interface with VBA"
          app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app_data.db"
      
          email.init_app(app)
          db.init_app(app)
          login_manager.init_app(app)
      
          # Import and set the blueprints/routes
          ...
      

      创建用户类并将登录设置为models.py

      from app import db, login_manager
      from flask_login import UserMixin
      
      @login_manager.user_loader
      def load_user(user_id):
          return User.query.filter_by(id=user_id).first()
      
      class User(UserMixin, db.Model):
          __tablename__ = 'user'
      
          email = db.Column(db.String, primary_key=True)
          password = db.Column(db.String, nullable=False)
          verified = db.Column(db.Boolean, default=False)
      

      然后去路由,比如views.py:

      from flask import request, current_app, abort, render_template, BluePrint
      
      # Import your custom instances and models
      from app import email, db
      from models import User
      
      auth_page = Blueprint('auth', __name__)
      
      @auth_page.route("/create-user", methods=["GET", "POST"])
      def create_user():
          if request.method == "GET":
              return render_template("create_user.html")
          elif request.method == "POST":
              # Now we create the user
      
              # Getting form data (what user inputted)
              data = request.form.to_dict()
              email = data["email"]
              password = data["password"]
      
              # Verifying the user does not exist
              old_user = User.query.filter_by(id=email).first()
              if old_user:
                  abort(403)
      
              # Encrypt the password here (for example with Bcrypt)
              ...
      
              # Creating the user
              user = User(
                  email=email, 
                  password=password,
                  verified=False
              )
              db.session.add(user)
              db.session.commit()
      
              # Create a secure token (string) that identifies the user
              token = jwt.encode({"email": email}, current_app.config["SECRET_KEY"])
              
              # Send verification email
              email.send(
                  subject="Verify email",
                  receivers=email,
                  html_template="email/verify.html",
                  body_params={
                      "token": token
                  }
              )
      

      然后我们创建电子邮件正文。默认情况下,Flask-Redmail 从应用程序的 Jinja 环境中寻找 HTML 模板。只需创建文件templates/email/verify.html

      <h1>Hi,</h1>
      <p>
          in order to use our services, please click the link below:
          <be>
          <a href={{ url_for('verify_email', token=token, _external=True) }}>verify email</a>
      </p>
      <p>If you did not create an account, you may ignore this message.</p>
      

      最后,我们创建一个路由来处理验证:

      @auth_page.route("/vefify-email/<token>")
      def verify_email(token):
          data = jwt.decode(token, current_app.config["SECRET_KEY"])
          email = data["email"]
      
          user = User.query.filter_by(email=email).first()
          user.verified = True
          db.session.commit()
      

      请注意,您需要在 templates/create_user.htmlmodels.py 中存储您的 User 类。

      一些相关链接:

      更多关于红色邮件:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-06-27
        • 1970-01-01
        • 1970-01-01
        • 2019-05-28
        • 2011-08-12
        • 2016-02-29
        相关资源
        最近更新 更多