[英]Flask-Mail [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123)
I'm developing a Flask application that implements a user registration system.我正在开发一个实现用户注册系统的 Flask 应用程序。 The application uses Flask-Mail and itsdangerous to confirm a user's registration and reset their password via email.该应用程序使用 Flask-Mail 和 itsdangerous 来确认用户的注册并通过 email 重置他们的密码。 I configured Flask-Mail to use the recommended server settings provided by the email host that I'm using.我将 Flask-Mail 配置为使用我正在使用的 email 主机提供的推荐服务器设置。
MAIL_PORT = 587
MAIL_USE_SSL = False
MAIL_USE_TLS = True
At first, things were working fine;起初,一切正常。 I could submit emails without any issues.我可以毫无问题地提交电子邮件。 However, seemingly without changing any configuration settings, I now receive the following error when attempting to submit an email using Flask-Mail:但是,似乎没有更改任何配置设置,我现在在尝试使用 Flask-Mail 提交 email 时收到以下错误:
[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123) [SSL: WRONG_VERSION_NUMBER] 错误的版本号 (_ssl.c:1123)
I'm not sure where the problem is, and I am wondering if something has changed on the email provider's end?我不确定问题出在哪里,我想知道 email 提供商端是否发生了变化? I have tried setting MAIL_PORT = 25
with MAIL_USE_SSL=False
and MAIL_USE_TLS=False
;我尝试使用MAIL_USE_SSL=False
和MAIL_USE_TLS=False
设置MAIL_PORT = 25
; and, MAIL_PORT = 465
with MAIL_USE_SSL=True
and MAIL_USE_TLS=False
as well.并且, MAIL_PORT = 465
,同时MAIL_USE_SSL=True
和MAIL_USE_TLS=False
。 Using the former, I receive the same error as with port 587, but using the latter I'm receiving STARTTLS extension not supported by server.
使用前者,我收到与端口 587 相同的错误,但使用后者我收到STARTTLS extension not supported by server.
I'm running the Flask app in development mode at localhost:5000.我在 localhost:5000 以开发模式运行 Flask 应用程序。 Here's some of my configuration settings and code:这是我的一些配置设置和代码:
config.py配置文件
SECRET_KEY = 'verysecret'
MAIL_SERVER = "smtp.mymailservice.com"
MAIL_PORT = 587
MAIL_USE_SSL = False
MAIL_USE_TLS = True
MAIL_USERNAME = "myemail@myhostname.com"
MAIL_PASSWORD = "mypassword"
MAIL_DEFAULT_SENDER = 'Brand <noreply@myhostname.com>'
app/mailing.py应用程序/mailing.py
from flask_mail import Message
from flask import current_app
from .extensions import mail
def send_email(to, subject, template):
msg = Message(
subject,
recipients=[to],
html=template,
sender=current_app.config["MAIL_DEFAULT_SENDER"]
)
mail.send(msg)
app/users/routes.py应用程序/用户/routes.py
(One of the routes where I receive the error) (我收到错误的路线之一)
from flask import (
render_template, session, request, redirect, url_for, g, jsonify, flash
)
import uuid
from passlib.hash import sha256_crypt
from app.mailing import send_email
from app.extensions import db
from app.users import bp
from app.users.forms import *
from app.users.models import *
from app.users.token import *
@bp.route('/register', methods=['POST', 'GET'])
def register():
# Initialize the Register Form
form = RegisterForm()
# If the submitted form is valid
if form.validate_on_submit():
# Check to see if a user already exists with this email address
user = User.query.filter_by(email=form.email.data).first()
# If there is not a user with this email address, create a new user
if not user:
new_user = User(public_id=str(uuid.uuid4()),
email=form.email.data,
password=sha256_crypt.encrypt(
(form.password.data)),
first_name=form.firstname.data,
last_name=form.lastname.data
)
db.session.add(new_user)
db.session.commit()
token = generate_confirmation_token(new_user.email)
confirm_url = url_for("users.confirm_email",
token=token, _external=True)
html = render_template('confirm_email.html',
confirm_url=confirm_url)
subject = "Please confirm your email"
try:
send_email(new_user.email, subject, html)
flash("A confirmation email has been sent to you. Please verify your email address to activate your account.", category="success")
except Exception as e:
flash(
"There was a problem sending the confirmation email. Please try again later.", category="danger")
print(e)
session["user_id"] = new_user.public_id
session["email"] = new_user.email
session["name"] = new_user.first_name
flash("Thanks for registering!", category="success")
return redirect(url_for('users.unconfirmed'))
else:
flash("There is already an account associated with this email address. Log in, or use a different email address.")
return render_template("register_user.html", form=form)
app/extensions.py应用程序/扩展程序.py
from flask_mail import Mail
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
bootstrap = Bootstrap()
mail = Mail()
app/ init .py应用程序/初始化.py
from flask import Flask
from config import Config, DevelopmentConfig
from .errors import (
page_not_found, forbidden, internal_server_error
)
from .extensions import (
db, mail, bootstrap
)
def create_app(config_class=DevelopmentConfig):
app = MyFlask(__name__)
# Set Configuration
app.config.from_object(config_class)
# Register extensions
# Initialize Boostrap-Flask
bootstrap.init_app(app)
# Initialize Flask-SQLAlchemy
db.init_app(app)
# Initialize Flask-Mail
mail.init_app(app)
# Register error views
app.register_error_handler(404, page_not_found)
app.register_error_handler(403, forbidden)
app.register_error_handler(500, internal_server_error)
with app.app_context():
# register blueprints
from app.main import bp as bp_main
app.register_blueprint(bp_main)
from app.users import bp as bp_users
app.register_blueprint(bp_users)
return app
This answer was almost there but not quite for me.这个答案几乎就在那里,但对我来说并不完全。 Even more TL:DR.更多 TL:DR。
Put this in your config.py
file and forget about the rest...把它放在你的config.py
文件中,忘记 rest...
class Config:
MAIL_USE_TLS = True
MAIL_USE_SSL = False
More details...更多细节...
You probably have a config.py
file that looks like this:您可能有一个如下所示的config.py
文件:
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = 'sqlite:///site.db' # os.environ.get('DATABASE_URI')
MAIL_SERVER = os.environ.get('MAIL_SERVER')
MAIL_PORT = os.environ.get('MAIL_PORT')
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS')
MAIL_USE_SSL = os.environ.get('MAIL_USE_SSL')
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
MAIL_DEFAULT_SENDER = os.environ.get('MAIL_DEFAULT_SENDER')
And then you think you can have something like this in your VSCode debug config or the environment of your server:然后你认为你可以在你的 VSCode 调试配置或你的服务器环境中有这样的东西:
"env": {
"MAIL_USE_SSL":"true",
"MAIL_USE_TLS":"true",
Well that doesn't work because of the answer from @serrobit because "true" in VSCode turns into a str
instead of a Python True
.好吧,由于@serrobit 的回答,这不起作用,因为 VSCode 中的“true”变成了str
而不是 Python True
。
So back to the start, hard code TLS to True and SSL to False in the config.py file and go spend time on something useful.回到开头,在 config.py 文件中将 TLS 硬编码为 True,将 SSL 硬编码为 False,go 将时间花在有用的东西上。
I figured out what was going on.我弄清楚发生了什么事。 Evidently, you can pass in non-boolean types for MAIL_USE_TLS and MAIL_USE_SSL when initializing the Mail object from Flask-Mail.显然,在从 Flask-Mail 初始化 Mail object 时,您可以为 MAIL_USE_TLS 和 MAIL_USE_SSL 传入非布尔类型。 This becomes a problem when the Connection object calls configure_host() and conditionally checks if self.mail.use_ssl
.当 Connection object 调用 configure_host() 并有条件地检查if self.mail.use_ssl
时,这将成为一个问题。
Thus, as long as self.mail.use_ssl
is not None
, the method will set host = smtplib.SMTP_SSL(self.mail.server, self.mail.port)
, which in my case, lead to [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123) because mail.port
was set to 587.因此,只要self.mail.use_ssl
不是None
,该方法就会设置host = smtplib.SMTP_SSL(self.mail.server, self.mail.port)
,在我的情况下,这会导致 [SSL: WRONG_VERSION_NUMBER] 错误版本号 (_ssl.c:1123) 因为mail.port
设置为 587。
tl;dr Ensure the configuration variables for your Flask app are set to the appropriate type, especially if you are using environment variables, as those will always be of type str when accessing them via the os.environ dict. tl;dr 确保您的 Flask 应用程序的配置变量设置为适当的类型,尤其是在您使用环境变量时,因为当通过 os.environ dict 访问它们时,它们将始终为 str 类型。
flask_mail.py烧瓶邮件.py
class Connection(object):
"""Handles connection to host."""
def __init__(self, mail):
self.mail = mail
def __enter__(self):
if self.mail.suppress:
self.host = None
else:
self.host = self.configure_host()
self.num_emails = 0
return self
def __exit__(self, exc_type, exc_value, tb):
if self.host:
self.host.quit()
def configure_host(self):
## PROBLEM OCCURRED HERE BECAUSE type(self.mail.use_ssl) = <class 'str'> ##
if self.mail.use_ssl:
host = smtplib.SMTP_SSL(self.mail.server, self.mail.port)
else:
host = smtplib.SMTP(self.mail.server, self.mail.port)
host.set_debuglevel(int(self.mail.debug))
if self.mail.use_tls:
host.starttls()
if self.mail.username and self.mail.password:
host.login(self.mail.username, self.mail.password)
return host
Change this in your config.py
:在你的config.py
中改变它:
class Config:
MAIL_USE_TLS = bool(strtobool(os.environ.get('MAIL_USE_TLS', 'False')))
MAIL_USE_SSL = bool(strtobool(os.environ.get('MAIL_USE_SSL', 'False')))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.