您的位置:首页 > 其它

flask注册之后邮箱确认功能的实现

2016-11-05 23:13 357 查看
搞了半天终于可以正常运行了,但是还是感觉一团浆糊,总结一下,用户端要完成的操作是这样的:

1,注册(这时候数据库已经有用户的账户密码了,但是User.confirmed=False,这时是无法正常登陆的)

2,点开Email验证地址之前登陆(作者构建了一个中间状态路由,当用户注册之后但未确认时登陆就会被重定向到这个路由,这时APP保存了用户的登录状态(但是没有正常权限),之后再点击邮箱里的验证链接就可以验证成功了,不登录的话点击验证会抛出401错误的,因为验证路由(confirm)被@login_required上锁的,如果去掉@login_required的话也会报错,因为我们需要验证链接中的验证信息是否与注册的ID相同,相同才能注册,当然了,我们也可以直接拿链接中的验证信息解码之后的ID与数据库进行交互激活账号,但是这样安全系数较差,最好的办法是在用户注册之后实现自动登录,这样对用户比较友好),验证成功之后网页跳转到主页。

在用户操作的过程中,应用程序是怎么运行的?

1,接收用户的GET请求返回注册页面

2,用户填写资料之后接收用户POST请求重定向到登录页面

@auth.route('/register', methods=['GET', 'POST'])
def register():
#注册
form = RegistrationForm()
if form.validate_on_submit():
user = User(email=form.email.data,
username=form.username.data,
password=form.password.data)
db.session.add(user)
db.session.commit()  #这里不能等数据库自动保存,因为用户在验证时需要登录
token = user.generate_confirmation_token()  #生成HASH码
send_email(user.email, 'Confirm Your Account',
'auth/email/confirm', user=user, token=token)#发送验证信息
flash('邮件已经发送!')
return redirect(url_for('auth.login'))#重定向到登录页面
return render_template('auth/register.html', form=form)


在执行这一步的时候完成了以下两步操作

2.1,生成验证信息()

models.py

...
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
...
class User(db.Model,UserMixin):
...

def generate_confirmation_token(self,expiration=3600):
s = Serializer(current_app.config['SECRET_KEY'], expiration)
#这个函数需要两个参数,一个密匙,从配置文件获取,一个时间,这里1小时
return s.dumps({'confirm':self.id})
#为ID生成一个加密签名,然后再对数据和签名进行序列化,生成令牌版字符串(就是一长串乱七八糟的东西),然后返回
...


2.2,组装,发送验证信息()

email.py

#encoding:utf8
from . import mail
from flask_mail import Message
from flask import render_templat
4000
e, current_app

def send_email(to, subject, template, **kwargs):
app = current_app._get_current_object()#这里不太懂,牵扯到异步,反正是配置验证信息的
msg = Message(subject,sender='418836702@qq.com',recipients=[to])#实例化一个Message对象,准备发送邮件,接受者为to
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)#将准备好的模板添加到msg对象上,字典传的参里包括token(即生成的一长串字符串),链接的组装,页面的渲染在里面用jinja2语法完成
with app.app_context():
mail.send(msg) #发射


这时用户的页面已经跳转到登录页面,并且收到了邮件,假设他没瞎填,并且是一个很聪明的人,他这时就登录了,他登录之后程序干了什么呢?

3,首先肯定不可能让他直接登录成功,因为没激活,先滚去中间页面,这里作者用到了钩子,一般我们用@before_request,但是在蓝本全局中使用的话还是得用@before_app_request,不要问为什么,我也不知道,这个钩子的代码如下:

views.py
@auth.before_app_request
def before_request():
if current_user.is_authenticated \
and not current_user.confirmed \
and request.endpoint[:5] != 'auth.' \
and request.endpoint != 'static':
return redirect(url_for('auth.unconfirmed'))


这个钩子的作用是相当于梁山好汉,此路是我开,你满足我的条件你就听我的,这里三个条件:a,有用户登录着呢。b,这个用户没有验证,cd,是请求链接的端点信息,如果是这些东西赶紧放人家过去,人家有正事要干。最后跳转到这个路由:

@auth.route('/unconfirmed')
def unconfirmed():
if current_user.is_anonymous or current_user.confirmed:
return redirect(url_for('main.index'))
return render_template('auth/unconfirmed.html')


这就是那个中间路由,就像你进我家但是你没有激活,我把你关院子里不让你进来一样,但是这时程序中已经有你的登录session了,这个时候你再点击邮件里的链接就可以进房间了,并且执行激活操作了:

@auth.route('/confirm/<token>')
@login_required
def confirm(token):
if current_user.confirmed:
#如果你已经激活过了
return redirect(url_for('main.index'))
if current_user.confirm(token):
#去激活并且激活成功了
flash('O了!')
else:
flash('你是盗号的还是迟到鬼?')
return redirect(url_for('main.index'))


这一步中执行了激活操作:

models.py

class User(db.Model,UserMixin):
...
def confirm(self,token):
#激活
s = Serializer(current_app.config['SECRET_KEY'])
#传入和刚才一样的密匙,解码要用
try:
data = s.loads(token)#解码
except:
return False
if data.get('confirm') != self.id:
return False
self.confirmed = True #解的码和已经登录的账号ID相等时操作数据库改变User.confirmed的值为True
db.session.add(self)
return True #激活成功返回True
...


齐活!

PS:flask-email的难点主要是在配置上,我这里用的是QQ邮箱,很久之前搞的,这里直接拿来用,改天总结下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: