11.4 重置密码
我们使用flask_mail来完成电子邮件的发送
pipenv install flask-mail
1.在app中注册flask-mail
mail = Mail()
mail.init_app(mail)
2.EMAIL配置
# email配置
MAIL_SERVER = 'smtp.qq.com'
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USE_TSL = False
MAIL_USERNAME = '1152057576@qq.com'
# QQ邮箱->设置->账户->[POP3...]->生成授权码->发送短信->获取授权码
MAIL_PASSWORD = 'pstomjiomwyybadh'
3.编写邮件工具类
def send_email(to, subject, template, **kwargs):
msg = Message(
subject,
sender=current_app.config['MAIL_USERNAME'],
recipients=[to])
# 发送一封HTML邮件
mail.html = render_template(template, kwargs)
mail.send(msg)
4.测试调用
send_email(account_email, "重置你的密码", 'email/reset_password.html',
user=user, token='aaa')
5.使用itsdangerous生成token
我们的token应该有一个过期时间,应该可以存储我们想要存储的值,flask为我们提供了一个非常好用的插件itsdangerous
def generate_token(self, expiration=600):
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
s = Serializer(secret_key=current_app.config['SECRET_KEY'], expires_in=expiration)
# s.dumps生成的是byte数组,我们需要编码成字符串
return s.dumps({'id': self.id}).decode('utf-8')
6.重置密码
@classmethod
def reset_password(cls, token, new_password):
s = Serializer(secret_key=current_app.config['SECRET_KEY'])
try:
# 解析token获取用户id,方法与生成token相反
data = s.loads(token.encode('utf-8'))
except:
return False
uid = data.get(id)
with db.auto_commit():
# 获取用户信息并修改
user = User.query.get_or_404(uid)
user.password = new_password
return True
7.视图函数编写
@web.route('/reset/password/<token>', methods=['GET', 'POST'])
def forget_password(token):
form = ResetPasswordForm(request.form)
if request.method == 'POST' and form.validate():
success = User.reset_password(token, form.password1.data)
if success:
flash('您的密码已重置,请使用新密码登录')
return redirect(url_for('web.login'))
flash('密码重置失败')
return render_template('auth/forget_password.html')
8.发送邮件优化,异步发送
def send_mail_async(app, msg):
# App_Context 的栈Local Stack是线程隔离的,在新线程里栈顶为空,需要手动入栈
with app.app_context():
try:
mail.send(msg)
except:
print('邮件发送失败')
def send_mail(to, subject, template, **kwargs):
msg = Message(
'[鱼书]'+' '+subject,
sender=current_app.config['MAIL_USERNAME'],
recipients=[to])
msg.html = render_template(template, **kwargs)
# current_app是代理对象,在当前线程下有指向,但是在新开启的线程中就没了,因为LocalProxy是线程隔离的
app = current_app._get_current_object()
thr = Thread(target=send_mail_async, args=[app,msg])
thr.start()
Last updated