Flask-Mail [SSL: WRONG_VERSION_NUMBER] 版本号错误 (_ssl.c:1123)
Flask-Mail [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123)
我正在开发一个实现用户注册系统的 Flask 应用程序。该应用程序使用 Flask-Mail 和 itsdangerous 来确认用户的注册并通过电子邮件重置他们的密码。我将 Flask-Mail 配置为使用我正在使用的电子邮件主机提供的推荐服务器设置。
MAIL_PORT = 587
MAIL_USE_SSL = False
MAIL_USE_TLS = True
起初,一切正常;我可以毫无问题地提交电子邮件。然而,似乎没有更改任何配置设置,我现在在尝试使用 Flask-Mail 提交电子邮件时收到以下错误:
[SSL: WRONG_VERSION_NUMBER] 版本号错误 (_ssl.c:1123)
我不确定问题出在哪里,我想知道电子邮件提供商是否发生了某些变化?我尝试用 MAIL_USE_SSL=False
和 MAIL_USE_TLS=False
设置 MAIL_PORT = 25
; MAIL_PORT = 465
和 MAIL_USE_SSL=True
以及 MAIL_USE_TLS=False
。使用前者,我收到与端口 587 相同的错误,但使用后者,我收到 STARTTLS extension not supported by server.
我是 运行 localhost:5000 处于开发模式的 Flask 应用程序。这是我的一些配置设置和代码:
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
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
(我收到错误的路线之一)
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
from flask_mail import Mail
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
bootstrap = Bootstrap()
mail = Mail()
app/init.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
我明白是怎么回事了。显然,当从 Flask-Mail 初始化 Mail 对象时,您可以为 MAIL_USE_TLS 和 MAIL_USE_SSL 传递非布尔类型。当 Connection 对象调用 configure_host() 并有条件地检查 if self.mail.use_ssl
.
时,这会成为一个问题
因此,只要 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;博士
确保将 Flask 应用程序的配置变量设置为适当的类型,尤其是当您使用环境变量时,因为当通过 os.environ dict.
访问它们时,它们的类型始终为 str
flask_mail.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
这个答案几乎已经有了,但对我来说还不够。甚至 TL:DR.
将其放入您的 config.py
文件中,其他的就不用管了...
class Config:
MAIL_USE_TLS = True
MAIL_USE_SSL = False
更多详情...
您的 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')
然后你认为你可以在你的 VSCode 调试配置或你的服务器环境中有这样的东西:
"env": {
"MAIL_USE_SSL":"true",
"MAIL_USE_TLS":"true",
嗯,由于@serrobit 的回答,这不起作用,因为 VSCode 中的“true”变成了 str
而不是 Python True
。
所以回到开始,在 config.py 文件中将 TLS 硬编码为 True 并将 SSL 硬编码为 False,然后花时间做一些有用的事情。
在您的 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')))
我正在开发一个实现用户注册系统的 Flask 应用程序。该应用程序使用 Flask-Mail 和 itsdangerous 来确认用户的注册并通过电子邮件重置他们的密码。我将 Flask-Mail 配置为使用我正在使用的电子邮件主机提供的推荐服务器设置。
MAIL_PORT = 587
MAIL_USE_SSL = False
MAIL_USE_TLS = True
起初,一切正常;我可以毫无问题地提交电子邮件。然而,似乎没有更改任何配置设置,我现在在尝试使用 Flask-Mail 提交电子邮件时收到以下错误:
[SSL: WRONG_VERSION_NUMBER] 版本号错误 (_ssl.c:1123)
我不确定问题出在哪里,我想知道电子邮件提供商是否发生了某些变化?我尝试用 MAIL_USE_SSL=False
和 MAIL_USE_TLS=False
设置 MAIL_PORT = 25
; MAIL_PORT = 465
和 MAIL_USE_SSL=True
以及 MAIL_USE_TLS=False
。使用前者,我收到与端口 587 相同的错误,但使用后者,我收到 STARTTLS extension not supported by server.
我是 运行 localhost:5000 处于开发模式的 Flask 应用程序。这是我的一些配置设置和代码:
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
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
(我收到错误的路线之一)
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
from flask_mail import Mail
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
bootstrap = Bootstrap()
mail = Mail()
app/init.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
我明白是怎么回事了。显然,当从 Flask-Mail 初始化 Mail 对象时,您可以为 MAIL_USE_TLS 和 MAIL_USE_SSL 传递非布尔类型。当 Connection 对象调用 configure_host() 并有条件地检查 if self.mail.use_ssl
.
因此,只要 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;博士 确保将 Flask 应用程序的配置变量设置为适当的类型,尤其是当您使用环境变量时,因为当通过 os.environ dict.
访问它们时,它们的类型始终为 strflask_mail.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
这个答案几乎已经有了,但对我来说还不够。甚至 TL:DR.
将其放入您的 config.py
文件中,其他的就不用管了...
class Config:
MAIL_USE_TLS = True
MAIL_USE_SSL = False
更多详情...
您的 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')
然后你认为你可以在你的 VSCode 调试配置或你的服务器环境中有这样的东西:
"env": {
"MAIL_USE_SSL":"true",
"MAIL_USE_TLS":"true",
嗯,由于@serrobit 的回答,这不起作用,因为 VSCode 中的“true”变成了 str
而不是 Python True
。
所以回到开始,在 config.py 文件中将 TLS 硬编码为 True 并将 SSL 硬编码为 False,然后花时间做一些有用的事情。
在您的 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')))