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