Flask Web 开发 用户认证_5

本文详细介绍了Flask Web应用中用户注册后的邮箱确认过程,包括使用安全令牌防止非法确认,实现确认链接的生成和验证,以及处理确认邮件的发送和接收。此外,还讲解了在用户未确认状态下限制其访问特定页面的钩子函数用法。

用户认证章节真够长的,不过涉及到的内容确实多,继续上一章节

这部分讲到的是新用户注册以后,需要在邮箱里面点击链接进行确认,这个在我们平时网站注册新用户的时候,经常会碰到


确认邮件中最简单的确认链接是http://www.example.com/auth/confirm/<id> 这种形式的URL,其中id 是数据库分配给用户的数字id。用户点击链接后,处理这个路由的视图函
数就将收到的用户id 作为参数进行确认,然后将用户状态更新为已确认。但这种实现方式显然不是很安全,只要用户能判断确认链接的格式,就可以随便指定URL中的数字,从而确认任意账户。解决方法是把URL 中的id 换成将相同信息安全加密后得到的令牌。


(venv) $ python manage.py shell
>>> from manage import app
>>> from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

>>> s = Serializer(app.config['SECRET_KEY'], expires_in = 3600)                #expires_in表示这个秘钥令牌的有限时间是多久,秒为单位
>>> token = s.dumps({ 'confirm': 23 })                                                                   #dumps表示将{'confirm':23}转化成安全令牌
>>> token
'eyJhbGciOiJIUzI1NiIsImV4cCI6MTM4MTcxODU1OCwiaWF0IjoxMzgxNzE0OTU4fQ.ey ...'
>>> data = s.loads(token)                                                                                       #loads则表示解析这个令牌,等于反向解码
>>> data 
{u'confirm': 23}


接着,有了这个验证的东西,我们起码要为我们的模型添加这样一个属性

class User(UserMixin,db.Model):
__tablename__='users'
id=db.Column(db.Integer,primary_key=True)
email=db.Column(db.String(64),unique=True,index=True)
username=db.Column(db.String(64),unique=True,index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
password_hash=db.Column(db.String(128))
confirmed = db.Column(db.Boolean,default=False)             #默认属性是False,也就是未认证

def generate_confirmation_token(self,expiration=3600):
s=Serializer(current_app.config['SECRET_KEY'],expiration)    #这句的作用不是很明白,为什么要导入秘钥,是不是使用这个功能必须要通过秘钥?
return s.dumps({'confirm':self.id})

def confirm(self,token):
s=Serializer(current_app.config['SECRET_KEY'])
try:
data=s.loads(token)      #尝试赋值给data......始终没明白什么情况下赋值会失败?token没有内容???
except:
return False
if data.get('confirm') !=self.id:  #如果数据内confirm对应的confirm值不是用户的唯一id号,则返回False
return False
self.confirmed = True       
db.session.add(self)              
return True


由于更新过数据库了,所以,需要新建一个脚本,并更新数据库


既然有了需要确认的这个新属性,那在注册之后,一般网站都会自动向你的邮箱发一份确认邮件,那这个功能就要添加在路由里面了

from ..email import send_email


@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)           #这里就要把user添加进数据库了,因为只有这样,才能有唯一的id生成
db.session.commit()            
token = user.generate_confirmation_token()
send_email(user.email,'Confirm Your Account','auth/email/confirm',user=user,token=token)
flash('A Confirmation email has been sent to you by email.')
return redirect(url_for('main.index'))
return render_template('auth/register.html',form=form)


之后,需要做的是,编辑邮件的内容,也就是你收到的邮件内容 auth/mail/confirm.html

<p>Dear {{ user.username }},</p>
<p>Welcome to <b>Flasky</b>!</p>
<p>To confirm your account please <a href="{{ url_for('auth.confirm', token=token, _external=True) }}">click here</a>.</p>  #这里生成的是超链接按钮
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
<p>{{ url_for('auth.confirm', token=token, _external=True) }}</p>            #一般来说url_for生成的是相对路径,有了_external=True以后,就可以生成包括http的全部路径了
<p>Sincerely,</p>                    #然后上一行,通过<p>来将整段验证代码显示出来,和平时我们注册网站时收到的邮件一模一样
<p>The Flasky Team</p>
<p><small>Note: replies to this email address are not monitored.</small></p>



邮件内容也编辑好了,我们再来看一下,如果我们点击了确认连接,返回的页面应该是什么样的

由于每个用户的id不一样,所以说,生成的token不一样,故这里的连接肯定是动态地址的

我们来编辑auth/views.py

@auth.route('/confirm/<token>')
@login_required                  #保护路由,要求你必须是在登陆状态才能访问这个页面
def confirm(token):
if current_user.confirmed:         #如果用户的状态已经是confirmed的了,那直接范围首页了
return redirect(url_for('main.index'))
if current_user.confirm:              #如果用户通过这个页面的访问,调用confirm函数并返回True了,那成功验证
flash('You have confirmed your account. Thanks!')
else:                                               #其他情况下,验证失败,并返回主页
flash('The confirmation link is invalid or has expired.')
return redirect(url_for('main.index'))


如我们平时访问的一些网站一样,当你还是个新手上路状态的账号时,你可能只能访问一些特定的区域,也做不了任何事。

这里就要用到前面章节说的钩子了,说实话,我还不是很理解钩子的原理,感觉需要去看上下文的源代码,不过先这么用起来吧。

对蓝本来说,before_request 钩子只能应用到属于蓝本的请求上。若想在蓝本中使用针对程序全局请求的钩子,必须使用before_app_request 修饰器。

@auth.before_app_request                          #这句的意思,字面上理解是,在app的request响应之前,先如何如何............
def before_request():
if current_user.is_authenticated \         #如果用户是认证过的
and not current_user.confirmed \         #但是confirmed属性是False
and request.endpoint[:5] !='auth.'and request.endpoint !='static':     #而且,request的网址不是以auth.static开头的
return redirect(url_for('auth.unconfirmed'))                                    #就返回到未确认的一个路由


@auth.route('/unconfirmed')
def unconfirmed():
if current_user.is_anonymous or current_user.confirmed:             #如果用户是非普通用户(is_anonymous对普通用户返回False),或者已确认的,则返回主页
return redirect(url_for('main.index'))
return render_template('auth/unconfirmed.html')             #不然,则进入未确认页面


同时满足以下3 个条件时,before_app_request 处理程序会拦截请求。
(1) 用户已登录(current_user.is_authenticated() 必须返回True)。
(2) 用户的账户还未确认。
(3) 请求的端点(使用request.endpoint 获取)不在认证蓝本中。访问认证路由要获取权
限,因为这些路由的作用是让用户确认账户或执行其他账户管理操作。


再来看一下未确认的页面unconfirmed.html如何渲染

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky - Login{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>You have not confirmed your account yet.</h1>
</div>
<p>
Before you can access this site you need to confirm your account . Check your inbox , you should have received an email with a confirmation link.
</p>
Need another confirmation email? 
<a href = "{{url_for('auth.resend_confirmation',token=token, _external=True)}}">Click here to send again</a>    #注意这里的写法,很容易写错的
{% endblock %}

效果如图




有时候,如果你的验证令牌已经过期了,那我们需要重新发送一份邮件来确认,那怎么办呢?

再做一个路由......来重新发送邮件

@auth.route('/confirm')
@login_required
def resend_confirmation():
token = current_user.generate_confirmation_token()
send_email(current_user.email, 'Confirm Your Account','auth/email/confirm', user=current_user, token=token)
flash('A new confirmation email has been sent to you by email')
return redirect(url_for('main.index'))



收到的Email效果如下,还挺有那么回事




确认完的邮件是这样的,不过页面内容还写的是未完成确认,不过应该不是大问题,渲染文件里面加个if语句应该就可以,后面再改吧,先这样。




这篇章终于快要结束了,还有最后一点管理用户,另外篇幅讲......

这认证用户一章节内容实在是太饱满了.....................................



评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值