这次的发送邮件是利用了Flask自带的邮件功能,而且利用了token的加密认证方式,会更加的安全(参考常见的认证方式)
可以参考阮一峰的博客,注意JWT的特点
总结一下就是JWT可以帮你把JSON信息用某种算法加密成一个令牌(token)然后每次登陆的时候用户就可以拿着这个令牌来登陆
1.1准备与测试
pip install PyJWT
>>> import jwt
>>> encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')
>>> encoded_jwt
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg'
>>> jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
{'some': 'payload'}
创建多线程发送更改密码的邮件
from email import encoders
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr, parseaddr
from flask import render_template
from threading import Thread
import smtplib
from_addr = "***********"
password = "*************"
smtplib_server = 'smtp.sina.com'
def send_regest_email(nickname,user_password,email):
msg = MIMEMultipart()
msg['From'] = Header(from_addr)
msg['Subject'] = Header('Bear Home Regist Information')
content = MIMEText(
"Your Bear Home username is %s,password is %s" % (
nickname.data, user_password.data),
'plain',
'utf-8'
)
msg.attach(content)
try:
server = smtplib.SMTP()
server.connect(smtplib_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, email.data, msg.as_string())
print("Successfully send")
except smtplib.SMTPException as e:
server.quit()
print('发送失败')
pass
def send_reset_email(html_body,to_email):
content = html_body
msg = MIMEMultipart()
msg['From'] = Header(from_addr)
msg['Subject'] = Header('Bear Home Reset Password Information')
msg.attach(MIMEText(content,'html','utf-8'))
try:
server = smtplib.SMTP()
server.connect(smtplib_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, to_email, msg.as_string())
print("Successfully send")
except smtplib.SMTPException as e:
server.quit()
print('发送失败')
pass
def send_password_reset_email(user,to_email):
token=user.get_reset_password_token()
html_body=render_template('emails/reset_password.html',user=user,token=token)
thread_01=Thread(target=send_reset_email,args=(html_body,to_email.data))
# send_reset_email( html_body=render_template('emails/reset_password.html',user=user,token=token),
# to_email=to_email.data
# )
thread_01.start()
这里的user.get_reset_password_token()就是获取更改密码的令牌,也就是加密后:
#以字符串形式生成一个JWT令牌
def get_reset_password_token(self,expires_in=600):
return jwt.encode(
{'reset_password':self.id,'exp':time()+expires_in},
app.config['SECRET_KEY'],algorithm='HS256').decode('utf-8')
exp
字段是JWTs的标准,如果它存在,则表示令牌的到期时间。 如果一个令牌有一个有效的签名,但是它已经过期,那么它也将被认为是无效的。 对于密码重置功能,我会给这些令牌10分钟的有效期。
app.config['SECRET_KEY']是秘密密钥用于创建加密签名
get_reset_password_token()
函数以字符串形式生成一个JWT令牌。 请注意,decode('utf-8')
是必须的,因为jwt.encode()
函数将令牌作为字节序列返回,但是在应用中将令牌表示为字符串更方便。
@app.route('/reset_password_request',methods=['GET', 'POST'])#, methods=['GET', 'POST']
def reset_password_request():
print("hahahha")
if current_user.is_authenticated:
return redirect(url_for('index'))
form = ResetPasswordRequestForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
print(user)
if user:
send_password_reset_email(user,form.email)
flash('Check your email for the instructions to reset your password')
return redirect(url_for('login'))
return render_template('reset_password_request.html',
title='Reset Password', form=form)
@app.route('/reset_password/', methods=['GET', 'POST'])
def reset_password(token):
if current_user.is_authenticated:
return redirect(url_for('index'))
user=User.verify_reset_password_token(token)
print("------------------------------------------------------------------")
print(token)
print(user)
if not user:
return redirect(url_for('index'))
form=ResetPasswordForm()
if form.validate_on_submit():
user.set_password(form.password.data)
db.session.commit()
flash('Your password has been reset.')
return redirect(url_for('login'))
return render_template('reset_password.html',form=form)
关于这个代码块最重要的是verify_reset_password_token函数:
@staticmethod
def verify_reset_password_token(token):
try:
id=jwt.decode(token,app.config['SECRET_KEY'],algorithms=['HS256'])['reset_password']#获取解码后的reset_password
except:
return
return User.query.get(id)#获取
解码token,获取id,返回对应的用户
填写邮箱
收到邮件
点击链接后就可以更改密码了