python表单验证wtf_使用Python的Flask框架表单插件Flask-WTF实现Web登录验证

# ourapp/forms.py

from flask_wtf import Form

from wtforms import StringField, PasswordField

from wtforms.validators import DataRequired, Email

class EmailPasswordForm(Form):

email = StringField('Email', validators=[DataRequired(), Email()])

password = PasswordField('Password', validators=[DataRequired()])

在 Flask-WTF 0.9 版本以前,Flask-WTF 提供了针对 WTForms 字段以及验证器的自己的封装。你可能看到外面一大堆的代码是从 flask.ext.wtforms 中不是从 wtforms 中导入 TextField,PasswordField。

在 Flask-WTF 0.9 版本以后,我们应该直接从 wtforms 中导入这些字段和验证器。

我们定义的表单是一个用户登录表单。我们把它叫做 EmailPasswordForm(),我们可以重用这个同样的表单类(Form)去做其它的一些事情,像注册表单。这里我们没有去定义一个又长又没有用的表单,而是选择一个很常用的表单,只是为了给你们介绍使用 Flask-WTF 定义表单的方式。也许以后在正式项目中会定义一个特别复杂表单。对于表单中包含字段名称,我们建议使用一个清楚的名称,并且在一个表单中保持唯一。不得不说,对于一个长的表单,我们可能要给出一个更符合上文的字段名称。

登录表单可以替我们做一些事情。它能够保证我们应用程序的安全以防止 CSRF 漏洞,验证用户输入并且渲染适当的标记,这些标记是我们为表单定义的字段。

CSRF 保护和验证CSRF 表示跨站请求伪造。CSRF 攻击是指第三方伪造(像一个表单提交)请求到一个应用程序的服务器。一个易受攻击的服务器假设从一个表单来的数据是来自它自己的网站并且采取相应的操作。

作为一个例子,比方说,一个邮件提供商可以让你通过提交一个表单来删除你的账号。表单发送一个 POST 请求到服务器上的 account_delete 端点并且当表单被提交的时候删除登录的账号。我们可以在自己的网站上创建一个表单,该表单发送一个 POST 请求到同一个 account_delete 端点。现在,如果我们让某人点击我们表单的提交按钮(或者通过 JavaScript 来这样做),邮件提供商提供的登录账号就会被删除掉。当然邮件提供商还不知道表单提交并不是发生在他们的网站上。

因此如何才能阻止 POST 请求来自别的网站?WTForms 通过在渲染每一个表单的时候生成一个唯一的令牌使得成为可能。生成的令牌会被传回到服务器,伴随着 POST 请求的数据,在表单被接受之前令牌必须接受服务器的验证。关键的是令牌是与存储在用户会话(cookies)的一个值有关并且会在一段时间后失效(默认是 30 分钟)。这种方式就能够保证提交一个有效表单的人就是加载页面的人(或者至少是使用同一电脑的人),而且他们只能在加载页面 30 分钟内这样做。

要开始使用 Flask-WTF 保护 CSRF,我们需要为我们的登录页定义一个视图。

# ourapp/views.py

from flask import render_template, redirect, url_for

from . import app

from .forms import EmailPasswordForm

@app.route('/login', methods=["GET", "POST"])

def login():

form = EmailPasswordForm()

if form.validate_on_submit():

# Check the password and log the user in

# [...]

return redirect(url_for('index'))

return render_template('login.html', form=form)

如果表单已经被提交和验证的话,我们可以继续登录的逻辑。如果它没有被提交的话(例如,只是一个 GET 请求),我们就要把表单对象传递给我们的模板,以便它能够被渲染。下面就是我们使用 CSRF 保护的时候模板的样子。

{# ourapp/templates/login.html #}

{% extends "layout.html" %}

{% endraw %}

Login Page{{ form.csrf_token }}

{% raw %}{{ form.csrf_token }}{% endraw %} 渲染了一个隐藏的字段,该字段包含那些奇特的 CSRF 令牌,并且当 WTForms 验证表单的时候会寻找这个字段。我们不用担心包含处理令牌的逻辑,WTForms 会主动帮我们去做。好哇!

自定义验证除了由 WTForms 提供的内置的表单验证器(例如,Required(),Email() 等等),我们能创建我们自己的验证器。我们将通过编写一个 Unique() 验证器来说明如何创建自己的验证器,Unique() 验证器是用来检查数据库并且确保用户提供的值在数据库中不存在。这能够用于确保用户名或者邮箱地址还没有使用。没有 WTForms 的话,我们可能要在视图中做这些事情,但是现在我们可以在表单本身做些事情。

现在我们来定义一个简单的注册表单,其实这个表单和登录的表单几乎一样。只是会在后面给它添加一些自定义的验证器。

# ourapp/forms.py

from flask_wtf import Form

from wtforms import StringField, PasswordField

from wtforms.validators import DataRequired, Email

class EmailPasswordForm(Form):

email = StringField('Email', validators=[DataRequired(), Email()])

password = PasswordField('Password', validators=[DataRequired()])

现在我们要添加我们的验证器用来确保它们提供的邮箱地址不存在数据库中。我们把这个验证器放在一个新的 util 模块,util.validators。

# ourapp/util/validators.py

from wtforms.validators import ValidationError

class Unique(object):

def __init__(self, model, field, message=u'This element already exists.'):

self.model = model

self.field = field

def __call__(self, form, field):

check = self.model.query.filter(self.field == field.data).first()

if check:

raise ValidationError(self.message)

这个验证器假设我们是使用 SQLAlchemy 来定义我们的模型。WTForms 期待验证器返回某种可调用的对象(例如,一个可调用的类)。

在 Unique() 的 \_\_init\_\_ 中我们可以指定哪些参数传入到验证器中,在本例中我们要传入相关的模型(例如,在我们例子中是传入 User 模型)以及要检查的字段。当验证器被调用的时候,如果定义模型的任何实例匹配表单中提交的值,它将会抛出一个 ValidationError。我们也可以添加一个具有通用默认值的消息,它将会被包含在 ValidationError 中。

现在我们可以修改 EmailPasswordForm,使用我们自定义的 Unique 验证器。

# ourapp/forms.py

from flask_wtf import Form

from wtforms import StringField, PasswordField

from wtforms.validators import DataRequired

from .util.validators import Unique

from .models import User

class EmailPasswordForm(Form):

email = StringField('Email', validators=[DataRequired(), Email(),

Unique(

User,

User.email,

message='There is already an account with that email.')])

password = PasswordField('Password', validators=[DataRequired()])

渲染表单WTForms 也能帮助我们为表单渲染成 HTML 表示。WTForms 实现的 Field 字段能够渲染成该字段的 HTML 表示,所以为了渲染它们,我们只必须在我们模板中调用表单的字段。这就像渲染 csrf_token 字段。下面给出了一个登录模板的示例,在里面我们使用 WTForms 来渲染我们的字段。

{# ourapp/templates/login.html #}

{% extends "layout.html" %}

Login Page{{ form.email }}

{{ form.password }}

{{ form.csrf_token }}

我们可以自定义如何渲染字段,通过传入字段的属性作为参数到调用中。

{{ form.email.label }}: {{ form.email(placeholder='yourname@email.com') }}

{% raw %}{{ form.password.label }}: {{ form.password }}{% endraw %}

{% raw %}{{ form.csrf_token }}{% endraw %}

处理 OpenID 登录现实生活中,我们发现有很多人都不知道他们拥有一些公共账号。一部分大牌的网站或服务商都会为他们的会员提供公共账号的认证。举个栗子,如果你有一个 google 账号,其实你就有了一个公共账号,类似的还有 Yahoo, AOL, Flickr 等。

为了方便我们的用户能简单的使用他们的公共账号,我们将把这些公共账号的链接添加到一个列表,这样用户就不用自手工输入了。

我们要把一些提供给用户的公共账号服务商定义到一个列表里面,这个列表就放到配置文件中吧 (fileconfig.py):

CSRF_ENABLED = True

SECRET_KEY = 'you-will-never-guess'

OPENID_PROVIDERS = [

{ 'name': 'Google', 'url': 'https://www.google.com/accounts/o8/id' },

{ 'name': 'Yahoo', 'url': 'https://me.yahoo.com' },

{ 'name': 'AOL', 'url': 'http://openid.aol.com/' },

{ 'name': 'Flickr', 'url': 'http://www.flickr.com/' },

{ 'name': 'MyOpenID', 'url': 'https://www.myopenid.com' }]

接下来就是要在我们的登录视图函数中使用这个列表了:

@app.route('/login', methods = ['GET', 'POST'])

def login():

form = LoginForm()

if form.validate_on_submit():

flash('Login requested for OpenID="' + form.openid.data + '", remember_me=' + str(form.remember_me.data))

return redirect('/index')

return render_template('login.html',

title = 'Sign In',

form = form,

providers = app.config['OPENID_PROVIDERS'])

我们从 app.config 中引入了公共账号服务商的配置列表,然后把它作为一个参数通过 render_template 函数引入到模板。

接下来要做的我想你也猜得到,我们需要在登录模板中把这些服务商链接显示出来。

{% extends "base.html" %}

{% block content %}

Sign In{{form.hidden_tag()}}

Please enter your OpenID, or select one of the providers below:

{{form.openid(size=80)}}

{% for error in form.errors.openid %}

[{{error}}]

{% endfor %}

|{% for pr in providers %}

{{pr.name}} |

{% endfor %}

{{form.remember_me}} Remember Me

{% endblock %}

这次的模板添加的东西似乎有点多。一些公共账号需要提供用户名,为了解决这个我们用了点 javascript。当用户点击相关的公共账号链接时,需要用户名的公共账号会提示用户输入用户名, javascript 会把用户名处理成可用的公共账号,最后再插入到 openid 字段的文本框中。

下面这个是在登录页面点击 google 链接后显示的截图:

本文原创发布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

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值