Flask Web 开发 用户认证_6
来源:互联网 发布:linux系统大全 编辑:程序博客网 时间:2024/05/19 00:52
握草,终于进入用户认证的最终章节了,觉得作者不错,到了这里,已经开始让你尝试自己写代码了
虽然在github上面 Miguelgrinberg 也放上了代码,不过还是尽量自己写吧
首先是对于已经注册认证的用户,他们有时候想修改密码,那我们肯定要为用户专门放一个页面,用来修改密码
那无非是做一个表单和页面,通过表单来链接数据库修改最后的密码
以我们的经验,一般这样的表单有3行,老密码,新密码,确认新密码
所以如下(本来form和路由都自己编名字了。。。。后来发现到后面和书对照起来太麻烦了。。。还是老实点先按照书来做吧。。。。)
class ChangePasswordForm(Form):
oldpassword=PasswordField('Oldpassword',validators=[Required()])
newpassword=PasswordField('Newpassowrd',validators=[Required(),EqualTo('newpassword2',message='Password must match.')])
newpassword2=PasswordField('Confirm password',validators=[Required()])
submit = SubmitField('Register')
而对应的路由设置如下:
@auth.route('/change_password',methods=['GET','POST']) #修改密码页面
@login_required #保护路由,说明要求是在登录状态才能操作
def change_password():
form = ChangePasswordForm()
if form.validate_on_submit():
if current_user.verify_password(form.oldpassword.data): #在表单提交有效的情况下,如果当前用户表单内输入的老密码验证返回结果是True
current_user.password=form.newpassword.data #则当前用户的密码更新为表单里面的newpassword(这里用到的是password.setter装饰器)
db.session(current_user) #提交更新
db.session.commit()
return redirect(url_for('main.index'))
else:
flash('Your oldpassword is wrong') #不然的话,出现提示消息,老密码错误
return render_template('auth/change_password.html',form = form)
这里需要注意的是:current_user实际上可以作为object来直接使用的,db.session.add(current_user)就可以显示这个作用
后端逻辑做完了,那前段页面也要做一个
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky-Change Password{% endblock %}
{% block page_content %}
<div class="page-header">
<h3>Please reset your password as below</h3>
</div>
{{wtf.quick_form(form)}} #快速装饰表单
{% endblock %}
效果图如下:
需要注意的是,我们这里添加了一个change password的按钮,这个超链接最好还是放在base.html里面
这样我们的密码修改功能就完成了
--------------------------------------------------------------------------功能分割线-----------------------------------------------------------------------------------------------
但是有时候你忘记了密码,那你连改密码都改不了
所以就要用到忘记密码功能了,这个功能的作用基本上就是------>通过邮箱发送给你一个链接------->点击链接进入修改密码页面--------->设置新密码
对于我们平时的经验来说,一般是通过邮箱先认证一下,再修改密码
那这样,等于是要有2个页面产生,一个是让你输入邮箱并发送邮件的页面,第二个是你邮箱点击链接返回过来的页面,可以修改密码
而且,需要做2个表单,一个是输入邮箱并发送的表单,另外一个是修改密码的表单
先来看要输入email地址的表单类
class PasswordResetRequestForm(Form):
email=StringField('Email Address',validators=[Required(),Length(1,64),Email()])
submit = SubmitField('Send out') #提交表单以发送EMAIL
再来看修改密码的表单类
class PasswordResetForm(Form):
email = StringField('Email Address',validators=[Required(),Email()]) #这一行特别注意,如果没有这一行的话,你到最后路由里面,没有办法定位你的具体账号的。
newpassword=PasswordField('Newpassowrd',validators=[Required(),EqualTo('newpassword2',message='Password must match.')])
newpassword2=PasswordField('Confirm password',validators=[Required()])
submit = SubmitField('Save Change')
def validate_email(self, field): #这里前面学过的东西差点又忘记了,以validate_开头的函数,会和普通验证函数一起被调用
if User.query.filter_by(email=field.data).first() is None:
raise ValidationError('Unknown email address.')
有几个新表单,那就有几个新页面,同时也要有几个新路由
我们接着看新路由
@auth.route('/reset_password',methods=['GET','POST']) #密码忘记通过邮件申请页面
def password_reset_request(): #名字这样取,容易记,是输入email地址的页面page
if not current_user.is_anonymous:
return redirect(url_for('main.index'))
form = SendResetEmail()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first() #通过表单上的email地址,查询数据库并把用户信息赋值给user
if user is None: #但是,不排除没有这个email地址的可能,所以,如果user返回的结果是None的话
flash('This email does not exist , please check again !') #出现提示消息,这个email不存在,请重新确认
else:
token = user.generate_reset_password_token() #如果用户存在,则生成一个修改密码的token,作用和前面的confirmation的token类似
send_email(user.email,'Reset Password','auth/email/rest_password',user=user,token=token,next=request.args.get('next'))
#EMAIL内容使用模板'auth/email/rest_password.html'来进行渲染,但是,最后为什么要加上next这个参数和内容,始终没搞懂
flash('Please check the email in your inbox !')
return render_template('auth/reset_password.html',form=form) #而整个输入email地址的页面,则通过send_reset_email_page.html来渲染
Email的渲染模板templates/auth/email/reset_password.html的内容,如下:
<p>Dear {{ user.username }},</p>
<p>Welcome to <b>Flasky</b>!</p>
<p>To reset your password please <a href="{{ url_for('auth.reset_password', 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.reset_password', token=token, _external=True) }}</p>
<p>Sincerely,</p>
<p>The Flasky Team</p>
<p><small>Note: replies to this email address are not monitored.</small></p>
模板内容,渲染PasswordResetRequestForm表单
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky-Reset Password Email{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Send Email to Reset</h1>
</div>
<div class='col-md-4'>
{{wtf.quick_form(form)}}
</div>
{% endblock %}
而上面我们用到一个函数,叫做generate_reset_password_token,这个函数的作用,其实和confirm里面的token作用是相似的
只是对于重新设置密码这个功能来说,我们是需要自己新建一个的,因为confirm里面最后的目标是修改confirmed属性,而我们这里不需要,我们最终目标是修改密码
那增加功能,就要在models里面修改了,如下:
def generate_reset_password_token(self,expiration=3600):
s=Serializer(current_app.config['SECRET_KEY'],expiration)
return s.dumps({'reset':self.id}) #注意,这里加密令牌时,key值自己定义为reset吧,这样逻辑功能比较清晰点。
def reset_password(self,token,new_password):
s=Serializer(current.config['SECRET_KEY'])
try:
data=s.loads(token)
except:
return False
if data.get('reset') !=self.id:
return False
self.password = new_password #这里,又用到了password.setter的装饰器,来重新设定密码
db.session.add(self)
return True
好,表单,模型,路由这些后端逻辑设置好了,接下去就是搞前段了
我觉得首先是和平时我们登录的网站一样,要有一个按钮来转到发送email这个页面,就像我们平时看到的 “忘记密码?”这样类似
那我决定把他加载login的页面上
效果图如下:
虽然代码写得有点ugly......5个<br>,不过位置图效果出来还是挺满意的,正好在输入密码的边上,挺人性化
#
#
# 这里留给收到的EMAIL邮件内容
#
#
收到EMAIL点击链接返回后
还需要我们配置一个路由,以及模板来显示修改密码的页面
这里贴2个版本,一个是原来我自己写的,另外一个是作者的源代码,我觉得源代码是不是有点重复的地方.......
先贴源代码
@auth.route('/reset/<token>', methods=['GET', 'POST'])
def password_reset(token):
if not current_user.is_anonymous: #这一句确实有必要,判断是否是可以登录的用户
return redirect(url_for('main.index'))
form = PasswordResetForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user is None: #其实这个根据EMAIL找不到用户的功能,我做在了发送EMAIL的时候,如果找不到user,连EMAIL也不给你发
return redirect(url_for('main.index'))
if user.reset_password(token, form.password.data): #这个我要说一句,本来我的想法是,只要是通过连接返回回来的页面,你通过passwordsetter装饰器来修改就是了,
#何必再多写一个函数来reset参数呢,后来我想想,作者这样写,把token带进来,也许是出于安全考虑
flash('Your password has been updated.')
return redirect(url_for('auth.login'))
else:
return redirect(url_for('main.index'))
return render_template('auth/reset_password.html', form=form)
讲到上面多写的函数,我们需要重新回到models里面,为User类添加内容
如下:
def reset_password(self,token,newpassword):
s=Serializer(current.config['SECRET_KEY'])
try:
data=s.loads(token)
except:
return False
if data.get('reset') != self.id:
return False
self.password = new_password
db.session.add(self)
db.session.commit()
return True
渲染的模板没啥好多说,只是修改下title而已
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky-Reset Password{% endblock %}
{% block page_content %}
<div class="page-header">
<h3>Please reset your password</h3>
</div>
{{wtf.quick_form(form)}}
{% endblock %}
#
#
# 这里留给页面的截图,回头补
#
#
----------------------------------------------------------------功能分割线----------------------------------------------------------------------------------------------------------------------
终于来到这一章节的最后一点了,申请更换注册的邮箱!
这和我们平时注册网站时候碰到的更换绑定的邮箱的功能是一样的
这章节的说明要求有点复杂,先看看源码是如何操作的,来分析下
首先,老规矩,要创建一个修改email地址的表单
class ChangeEmailForm(Form):
email = StringField('New email',validators=[Required(),Email()])
password = PasswordField('Password',validators=[Required(),Email()])
submit = SubmitField('Submit')
def validate_email(self,field): #同样的作用,以validate_开头的,会和一般的验证函数一起作用,这里主要是测试新email是否和原来一样
if User.query.filter_by(email=form.email.data).first():
raise ValidationError('Email already registered !') #一样的话则抛出一个报错,email已经被注册
随后,我认为是需要在models里面添加函数,来生成修改email用的token了
def generate_email_change_token(self,new_email,expiration=3600): #这里注意一下,都了一个new_mail的参数,还不知道作用,先往下看
s = Serializer(current_app.config['SECRET_KEY'],expiration)
return s.dumps({'change_email':self.id,'new_email':new_email})
def change_email(self,token): #这个更改绑定邮箱的确认函数,非常重要!因为和前面的都不一样,用到了很多判断语句,并且用到了新的模块hashlib
s=Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except:
return False
if data.get('change_email') != self.id: #如果在data中没有找到change_email,则return False 我个人理解用户身份验证
return False
new_email = data.get('new_email') #如果在data里面没有找到new_email,这个key,则return False.......这个有点自相矛盾啊。。。
if new_email is None: #如果用户没有输入new_email,则return False
return False
if self.query.filter_by(email = new_email).first() is not None: #如果以new_email来查询,和用户目前的email属性相同的话,则return False,意思重复了
return False
self.email = new_email #如果以上情况均通过,则更新 email属性的值,为new_email,即重新设定成功
self.avatar_hash = hashlib.md5(self.email.encode('utf-8')).hexdigest()
db.session.add(self)
db.session.commit()
return True
上面的代码最后部分,用到了hashlib的模块,我觉得廖雪峰老师的Python教程里面关于这部分的讲解非常详细了,还讲到了关于破解和反破解一方面的知识。
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319556588648dd1fb0047a34d0c945ee33e8f4c90cc000
所以,我们在models里面还需要import hashlib并为User类加入avatar_hash的类属性
avatar_hash的值就等于通过hashlib的md5函数,将email值变成摘要信息的内容.
模型部分的修改完成了,接着我们就要做最后一步,添加路由了
@auth.route('/change-email',methods=['GET','POST'])
@login_required
def change_email_request:()
form = ChangeEmailForm()
if form.validate_on_submit():
if current_user.verify_password(form.password.data): #如果用户在表单上输入的密码和数据库的匹配
new_mail = form.email.data #则将这个email地址赋值给new_email
token = current_user.generate_email_change_token(new_mail) #生成token
send_email(new_mail,'Confirm your email address','auth/email/change_email',user = current_user,token = token) #发送email,包含new_mail
return redirect('main.index')
else:
flash ('invalid password or email!')
return render_template('auth/change_email.html',form = form)
发送邮件的路由部分做完了以后,需要做模板来渲染
模板auth/change_email.html如下,没啥好多说的,改个title而已
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky-Change Email{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Change your email address</h1>
</div>
{{ wtf.quick_form(form) }}
{% endblock %}
如下是你收到的email的内容的渲染模板templates/auth/email/change_email.html
#
# 这里留给被渲染后的输入email和密码的表单画面
# 这里留给收到的email的内容截图
#
#
点击email的连接回来以后,就显示页面,后台逻辑检测是否修改成功,但是我个人认为,这样的功能应该放在发送邮件前就昨晚
而修改绑定email的返回页面则简单的多,因为逻辑上的修改只需要用current_user.change_email来执行,只要是True,就返回flash消息提示成功
@auth.route('/change-email/<token>',methods=['GET','POST'])
@login_required
def change_email(token):
if current_user.change_email(token):
flash('Your email address has been updated !')
else:
flash('Invalid request.')
return redirect(url_for('main.index'))
#
#
# 这里留给点击email以后返回的画面截图
#
#
终于.....................第八章结束了,太漫长了...............不过知识点很多,需要巩固,有些需要后期补看一下源代码才能理解。
-------------------------------------------------------------分割线:额外的知识点-----------------------------------------------------------------------------
这里碰到一个情况引发思考
如下图,当你在未登录的情况下,尝试点击change password进入此页面时候,他会有一个flash消息提示你,请登录后才有权限访问页面
但是,我查看了所有的路由函数,并没有找到设置这个flash消息的地方
那这个消息肯定是在哪个地方默认设置的咯?百度了一下,发现,他是在Flask-Login的login_view里面设置的,所以我回过头去看前面章节的设置
我们来看Flask-Login的官方文档,下面的红框部分
首先第一点:LoginManager.login_message:这里设置的就是默认的flash消息,而这个消息是用在user.login(我的例子里是auth.login)页面的。
如果需要修改的话,需要修改login_manager.login_message这个内容
第二点:在未登录状态下,尝试访问一个需要登录状态才能访问的页面时,login_view会放把尝试访问的页面的地址
最后,看一下我们书中的例子,书里面并没有很详细地讲到这一点,所以这个只是点被忽略了
-----------------------------------------------------------------------分割线:知识点结束----------------------------------------------------------------------------
- Flask Web 开发 用户认证_6
- Flask Web 开发 用户认证
- Flask Web 开发 用户认证_2
- Flask Web 开发 用户认证_3
- Flask Web 开发 用户认证_4
- Flask Web 开发 用户认证_5
- 《flask web开发》第八章 用户认证
- flask web开发-用户认证代码分析(三)
- flask-web开发-用户认证代码分析(四)
- [python3.6 flask web学习]Flask用户认证框架
- Flask Web 开发 用户角色
- Flask Web 开发 用户资料
- Flask Web 开发 用户评论
- Flask Web开发-用户认证部分代码分析(一)
- flask web开发-用户认证部分代码分析(二)
- Flask(8)-用户认证
- Flask:用户认证
- Flask Web 开发 用户资料_2
- SQL SERVER tablediff比较表是否一致工具
- 【二分图匹配】HDU1281-棋盘游戏
- Java的枚举类型使用方法详解
- 第一个hadoop入门程序WordCount
- String format的简单用法
- Flask Web 开发 用户认证_6
- Android自定义饼图TTJPieChart
- android 选择图片 裁剪 Fileprovider
- Leetcode 95. Unique Binary Search Trees II 二叉搜索树2 解题报告
- (五)c52学习之旅-静态数码管
- MKMapView的使用及定位自己当前的位置
- 嵌入式QT configure
- 当数字与字符串遇上‘+’和‘.’该如何处理
- c语言---运算符