python函数登录验证_在Python的Flask框架中验证注册用户的Email的方法-程序员宅基地

技术标签: python函数登录验证  

class User(db.Model):

__tablename__ = "users"

id = db.Column(db.Integer, primary_key=True)

email = db.Column(db.String, unique=True, nullable=False)

password = db.Column(db.String, nullable=False)

registered_on = db.Column(db.DateTime, nullable=False)

admin = db.Column(db.Boolean, nullable=False, default=False)

confirmed = db.Column(db.Boolean, nullable=False, default=False)

confirmed_on = db.Column(db.DateTime, nullable=True)

def __init__(self, email, password, confirmed,

paid=False, admin=False, confirmed_on=None):

self.email = email

self.password = bcrypt.generate_password_hash(password)

self.registered_on = datetime.datetime.now()

self.admin = admin

self.confirmed = confirmed

self.confirmed_on = confirmed_on

注意此区域是怎样默认成“False”的。还要添加个confirmed_on字段,那是个datetime。为了用队列分析来分析registered_on和confirmed_on日期的不同,所以我想包含这个datetime。

让我们完全从头开始创建数据库并迁移吧!所以,先删除数据库dev.sqlite,以及“迁移”文件夹。

控制命令

接下来,在manage.py中,更新create_admin命令,使新的数据库字段生效:

@manager.command

def create_admin():

"""Creates the admin user."""

db.session.add(User(

email="[email protected]",

password="admin",

admin=True,

confirmed=True,

confirmed_on=datetime.datetime.now())

)

db.session.commit()

要确保导入datetime。现在,先再一次运行下面的指令:

$ python manage.py create_db

$ python manage.py db init

$ python manage.py db migrate

$ python manage.py create_admin

register()视图函数

最后,在我们再次注册用户之前,我们需要改一下project/user/views.py中的register()视图函数……

user = User(

email=form.email.data,

password=form.password.data

)

改成下面的:

user = User(

email=form.email.data,

password=form.password.data,

confirmed=False

)

明白了吗?思考下为什么要把confirmed默认成False。

嗯好。再运行一遍应用。转入到http://localhost:5000/register,再注册一个新用户。如果你在SQLite浏览器中打开了你的SQLite数据库,你会看到:

那么,这个我新注册的用户,[email protected]没有被验证。让我们验证它。

添加email验证

产生验证令牌

邮件验证应包含一个特殊URL,使得用户只需简单地点击它,即可验证他/她的账户。理想情况下,这个URL应该看起来像这样–http://yourapp.com/confirm/。这里的关键是它id。在这个id中用itsdangerous包,编码用户的邮件(包含时间戳)。

创建一个叫project/token.py的文件,添加下面的代码:

# project/token.py

from itsdangerous import URLSafeTimedSerializer

from project import app

def generate_confirmation_token(email):

serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])

return serializer.dumps(email, salt=app.config['SECURITY_PASSWORD_SALT'])

def confirm_token(token, expiration=3600):

serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])

try:

email = serializer.loads(

token,

salt=app.config['SECURITY_PASSWORD_SALT'],

max_age=expiration

)

except:

return False

return email

所以在generate_confirmation_token()函数中,通过URLSafeTimedSerializer用在用户注册时得到的email地址生成一个令牌。那个_真实的_email在令牌中被编了码。确认令牌之后,在confirm_token()函数中,我们可以用loads()方法,它接管令牌和其过期时间——一个小时(3600秒)内有效——作为参数。只要令牌没过期,那它就会返回一个email。

在应用的配置中(BaseConfig()),确保添加SECURITY_PASSWORD_SALT:

SECURITY_PASSWORD_SALT = 'my_precious_two'

更新register()视图函数

现在再从project/user/views.py更新下register()视图函数:

@user_blueprint.route('/register', methods=['GET', 'POST'])

def register():

form = RegisterForm(request.form)

if form.validate_on_submit():

user = User(

email=form.email.data,

password=form.password.data,

confirmed=False

)

db.session.add(user)

db.session.commit()

token = generate_confirmation_token(user.email)

也是,要确保更新了这些导入模块:

from project.token import generate_confirmation_token, confirm_token

处理Email验证

接下来,添加个新的视图来解决email验证:

@user_blueprint.route('/confirm/')

@login_required

def confirm_email(token):

try:

email = confirm_token(token)

except:

flash('The confirmation link is invalid or has expired.', 'danger')

user = User.query.filter_by(email=email).first_or_404()

if user.confirmed:

flash('Account already confirmed. Please login.', 'success')

else:

user.confirmed = True

user.confirmed_on = datetime.datetime.now()

db.session.add(user)

db.session.commit()

flash('You have confirmed your account. Thanks!', 'success')

return redirect(url_for('main.home'))

把这个添加到project/user/views.py。同样,确保更新了这些导入:

import datetime现在我们通过令牌调用confirm_token()函数。如果成功,我们更新用户,把email_confirmed属性改成True, 设置datetime为验证发生的时间。还有,要是用户已经进行过一遍验证过程了——而且已经验证了——我们要提醒用户这点。

创建email模板

接着,添加一个基础email模板:

Welcome! Thanks for signing up. Please follow this link to activate your account:

Cheers!

把这个在“project/templates/user”中存为activate.html。这用了一个简单叫confirm_url的变量,它会在register()视图函数中被创建。

发送邮件

通过已经安装了而且设置在project/__init__.py中的Flask-Mail的一点儿帮助来创建一个基本函数来发送邮件。

创建一个叫email.py的文件:

# project/email.py

from flask.ext.mail import Message

from project import app, mail

def send_email(to, subject, template):

msg = Message(

subject,

recipients=[to],

html=template,

sender=app.config['MAIL_DEFAULT_SENDER']

)

mail.send(msg)

在“project”文件夹中存一下。

所以,我们只要简单地去处理收件人列表,主题,模板即可。我们会一点一点处理邮件配置的设置。

在project/user/views.py中(再次!)更新register()视图函数

@user_blueprint.route('/register', methods=['GET', 'POST'])

def register():

form = RegisterForm(request.form)

if form.validate_on_submit():

user = User(

email=form.email.data,

password=form.password.data,

confirmed=False

)

db.session.add(user)

db.session.commit()

token = generate_confirmation_token(user.email)

confirm_url = url_for('user.confirm_email', token=token, _external=True)

html = render_template('user/activate.html', confirm_url=confirm_url)

subject = "Please confirm your email"

send_email(user.email, subject, html)

login_user(user)

flash('A confirmation email has been sent via email.', 'success')

return redirect(url_for("main.home"))

return render_template('user/register.html', form=form)

还要添加下面的导入模块:

from project.email import send_email

我们在这里将所有的东西整合到一起。这个函数基本功能是作为控制器(直接或间接):

处理最初注册,

产生令牌和确认URL,

寄送确认email,

快速验证,

用户登入,

更改用户。

你注意到_external=True参数了吗?这增加了包含了hostname和port(在我们情况中,http://localhost:5000)的完整URL。

在我们测试这个之前,我们要去设定邮件设置。

邮件

在project/config.py中更新BaseConfig():

class BaseConfig(object):

"""Base configuration."""

# main config

SECRET_KEY = 'my_precious'

SECURITY_PASSWORD_SALT = 'my_precious_two'

DEBUG = False

BCRYPT_LOG_ROUNDS = 13

WTF_CSRF_ENABLED = True

DEBUG_TB_ENABLED = False

DEBUG_TB_INTERCEPT_REDIRECTS = False

# mail settings

MAIL_SERVER = 'smtp.googlemail.com'

MAIL_PORT = 465

MAIL_USE_TLS = False

MAIL_USE_SSL = True

# gmail authentication

MAIL_USERNAME = os.environ['APP_MAIL_USERNAME']

MAIL_PASSWORD = os.environ['APP_MAIL_PASSWORD']

# mail accounts

MAIL_DEFAULT_SENDER = '[email protected]'

查看 official Flask-Mail documentation 以得到更多信息如果你已经有了GMAIL账号,那你可以用它或者注册一个测试用GMAIL账户。然后把环境变量暂时设置在当前shell中:

$ export APP_MAIL_USERNAME="foo"

$ export APP_MAIL_PASSWORD="bar"

如果你GMAIL账号有两步授权, Google会屏蔽掉它。

现在开始测试!

第一个测试

打开应用,转入到 http://localhost:5000/register。然后用你能登陆的email地址注册。顺利的话,你应该会收到封email,看起来像这样:

点击URL,你会转到 http://localhost:5000/。保证用户在数据库里,那‘confirmed'字段是True,有个datetime和confirmed_on字段绑定在一起。

好!

处理许可

如果你记得,在教程的开始部分,我们决定了“未验证用户可以登录但是他们会立刻被转入一个页面——我们称之为/unconfirmed路径——提醒用户需要验证账户才能使用应用”。

所以,我们要——

添加/unconfirmed路径

添加unconfirmed.html模板

更新register()视图函数

创建装饰器

更新navigation.html模板

添加/unconfirmed路径

添加下面的路径project/user/views.py:

@user_blueprint.route('/unconfirmed')

@login_required

def unconfirmed():

if current_user.confirmed:

return redirect('main.home')

flash('Please confirm your account!', 'warning')

return render_template('user/unconfirmed.html')

你看过类似的代码,所以我们继续。

添加unconfirmed.html模板

{% extends "_base.html" %}

{% block content %}

Welcome!

You have not confirmed your account. Please check your inbox (and your spam folder) - you should have received an email with a confirmation link.

Didn't get the email? Resend.

{% endblock %}

在“project/templates/user”中,把这个存为 unconfirmed.html 。这次应该直截了当。现在,为了重新寄送验证email,就只加了一个假的URL。我们接下来会解决它的。

更新register()视图函数

现在简单地把:

return redirect(url_for("main.home"))

变成:

return redirect(url_for("user.unconfirmed"))

所以,送完验证email后,用户会进入/unconfirmed路径。

创建装饰器

# project/decorators.py

from functools import wraps

from flask import flash, redirect, url_for

from flask.ext.login import current_user

def check_confirmed(func):

@wraps(func)

def decorated_function(*args, **kwargs):

if current_user.confirmed is False:

flash('Please confirm your account!', 'warning')

return redirect(url_for('user.unconfirmed'))

return func(*args, **kwargs)

return decorated_function

这里我们用一个基本函数去检查用户是否验证。如果未验证,用户会进入/unconfirmed路径。在“project”目录中,把这个存为decorators.py。

现在装饰 profile() 视图函数:

@user_blueprint.route('/profile', methods=['GET', 'POST'])

@login_required

@check_confirmed

def profile():

... snip ...

确保导入装饰器:

from project.decorators import check_confirmed

更新 navigation.html 模板

最后,更新navigation.html模板接下来的部分——

把:

{% if current_user.is_authenticated() %}

变成:

{% if current_user.confirmed and current_user.is_authenticated() %}

又该测试了!

第二次测试

打开应用,再次用能登陆的email地址注册。(可以随便从数据库删除你之前注册的老用户,这样可以再用一遍)现在,注册完会转入http://localhost:5000/unconfirmed。

确保测试了http://localhost:5000/profile路径。这会使你转到http://localhost:5000/unconfirmed。

验证email,你就会有完整页面的权限了。快去把!

重寄送email

最后,来做重新寄送的链接。增加下面的视图函数到project/user/views.py:

@user_blueprint.route('/resend')

@login_required

def resend_confirmation():

token = generate_confirmation_token(current_user.email)

confirm_url = url_for('user.confirm_email', token=token, _external=True)

html = render_template('user/activate.html', confirm_url=confirm_url)

subject = "Please confirm your email"

send_email(current_user.email, subject, html)

flash('A new confirmation email has been sent.', 'success')

return redirect(url_for('user.unconfirmed'))

现在更新unconfirmed.html 模板:

{% extends "_base.html" %}

{% block content %}

Welcome!

You have not confirmed your account. Please check your inbox (and your spam folder) - you should have received an email with a confirmation link.

Didn't get the email? Resend.

{% endblock %}

第三次测试

你知道这次的演练内容了,这次保证去重新寄送一个新的确认email,测试链接。应该没问题。

最后,如果你给自己发好几个验证信,会发生什么? 每封都有效吗?测试一下。注册个新用户,寄送一些新的验证信。验证第一封试试。它可以吗?它应该可以。这样行吗?你是否认为要是新的寄出,其他email应该失效呢?

对这种事做些调查。测试下你用的其他网页应用。它们是怎么处理这种行为的?

更新测试套件

好的。这是为了主要功能。我们更新当前测试套件怎么样?因为它,嗯,有问题。

运行测试:

$ python manage.py test

你会看到下面的错误:

TypeError: __init__() takes at least 4 arguments (3 given)

要去改正它,只需要在project/util.py中更新setUp()方法:

def setUp(self):

db.create_all()

user = User(email="[email protected]", password="admin_user", confirmed=False)

db.session.add(user)

db.session.commit()

测试之前,在tests/test_models.py中,注释一下test_user_registration()测试,因为我们不想为了这个测试真 的发送一封邮件。

现在再测试一下。应该全能通过!

结论

还可以做更多的:

富文本 vs. 纯文本email——应该都寄出。

重设定密码email——当用户忘记密码时,这些应该被寄出。

用户管理——应该允许用户更新email和密码,当email变了的话,应该重新验证一遍。

测试——需要写更多测试来涉及更多新功能,包含用mock/patch更新的test_user_registration(),来防止实际email被发送。

本文原创发布php中文网,转载请注明出处,感谢您的尊重!

相关文章

相关视频

网友评论

文明上网理性发言,请遵守 新闻评论服务协议我要评论

立即提交

专题推荐独孤九贱-php全栈开发教程

全栈 100W+

主讲:Peter-Zhu 轻松幽默、简短易学,非常适合PHP学习入门

玉女心经-web前端开发教程

入门 50W+

主讲:灭绝师太 由浅入深、明快简洁,非常适合前端学习入门

天龙八部-实战开发教程

实战 80W+

主讲:西门大官人 思路清晰、严谨规范,适合有一定web编程基础学习

php中文网:公益在线php培训,帮助PHP学习者快速成长!

Copyright 2014-2020 https://www.php.cn/ All Rights Reserved | 苏ICP备2020058653号-1

  

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_39634052/article/details/111070292

智能推荐

while循环&CPU占用率高问题深入分析与解决方案_main函数使用while(1)循环cpu占用99-程序员宅基地

文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。​​​​​​while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99

【无标题】jetbrains idea shift f6不生效_idea shift +f6快捷键不生效-程序员宅基地

文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效

node.js学习笔记之Node中的核心模块_node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是-程序员宅基地

文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是

数学建模【SPSS 下载-安装、方差分析与回归分析的SPSS实现(软件概述、方差分析、回归分析)】_化工数学模型数据回归软件-程序员宅基地

文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件

利用hutool实现邮件发送功能_hutool发送邮件-程序员宅基地

文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件

docker安装elasticsearch,elasticsearch-head,kibana,ik分词器_docker安装kibana连接elasticsearch并且elasticsearch有密码-程序员宅基地

文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码

随便推点

Python 攻克移动开发失败!_beeware-程序员宅基地

文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware

Swift4.0_Timer 的基本使用_swift timer 暂停-程序员宅基地

文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停

元素三大等待-程序员宅基地

文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待

Java软件工程师职位分析_java岗位分析-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析

Java:Unreachable code的解决方法_java unreachable code-程序员宅基地

文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code

标签data-*自定义属性值和根据data属性值查找对应标签_如何根据data-*属性获取对应的标签对象-程序员宅基地

文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象

推荐文章

热门文章

相关标签