Flask框架——Flask-WTF表单:数据验证、CSRF保护
上篇文件中,我们学习了Flask框架——消息闪现,这篇文章我们学习Flask框架——Flask-WTF表单:数据验证、CSRF保护。
Flask-WTF
表单负责收集网页中的数据,是Web应用程序的基本功能。
Flask-WTF是Flask框架的一个扩展,用来处理表单,它封装了WTForms,其特点有:
- 能快速定义表单模板;
- 验证表单数据;
- 全局的csrf保护,能够保护所有表单免受跨站请求伪造(CSRF)的攻击;
- 与 Flask-Uploads 一起支持文件上传;
- 国际化集成。
在WTForm表单中,主要的功能有验证用户提交的数据合法性、快速渲染模板、CSRF保护、文件上传和验证码等。
其安装方式很简单,执行如下代码即可:
pip install flask-wtf
在安装flask-wtf的过程中,系统会自动安装wtform。
WTForms
表单字段
WTforms包中包含各种表单字段的定义,WTForms支持HTML的字段有:
字段 | 说明 |
---|---|
BooleanField | 复选框,值为True或False,相当于HTML的<input type ='checkbox'> |
DateField | 文本字段, 值为datetime.date格式 |
DateTimeField | 文本字段, 值为datetime.datetime格式 |
IntegerField | 文本字段, 值为整数 |
DecimalField | 用于显示带小数的数字的文本字段,值为decimal.Decimal |
FloatField | 文本字段, 值为浮点数 |
RadioField | 一组单选框 |
FileField | 文件上传字段 |
SelectField | 下拉列表 |
SelectMultipleField | 下拉列表, 可选择多个值 |
SubmitField | 表单提交按钮,相当于HTML的<input type="submit"> |
StringField | 文本字段,相当于HTML的<input type="text"> |
TextAreaField | 多行文本字段,相当于HTML的<input type="textarea"> |
HiddenField | 隐藏文本字段,相当HTML的<input type="hidden"> |
FormFiled | 把表单作为字段嵌入另一个表单 |
FieldList | 子组指定类型的字段 |
PasswordField | 密码文本字段,相当于HTML的<input type = 'password'> |
validators验证器
WTForms支持的validators验证器有:
验证函数 | 说明 |
---|---|
验证是电子邮件地址 | |
EqualTo | 比较两个字段的值; 常用于要求输入两次信息进行确认的情况 |
IPAddress | 验证IPv4网络地址 |
Length | 验证输入字符串的长度 |
NumberRange | 验证输入的值在数字范围内 |
Optional | 无输入值时跳过其它验证函数 |
DataRequired | 确保字段中有数据 |
Regexp | 使用正则表达式验证输入值 |
URL | 验证url |
AnyOf | 确保输入值在可选值列表中 |
NoneOf | 确保输入值不在可选列表中 |
注意:在使用上面的WTForms表单支持的HTML字段与验证函数之前,需要导入这些HTML字段与验证函数
好了,了解了WTForms表单支持的HTML字段与验证函数后,接下来我们通过示例代码来演示表单数据验证。
数据验证
创建一个Flask项目并在项目中创建一个名为form.py的表单类文件,当然文件名可以是任意的,在form.py文件中写入以下代码:
from flask_wtf import FlaskForm #导入FlaskForm
from wtforms import StringField, PasswordField #导入需要的字段
from wtforms.validators import DataRequired, length #导入需要的验证函数
class MyForm(FlaskForm):
name = StringField('name', validators=[DataRequired()]) #使用文本字段,数据不能为空验证
password=PasswordField('password',validators=[DataRequired(),length(min=6,max=12)]) #使用密码文本字段,length长度验证
导入我们需要的FlaskForm、字段与验证函数,这里我们使用了StringField、PasswordField字段和DataRequired数据不能为空验证函数、length长度验证函数。
当然我们可以在字段中添加多个验证函数,只需要在validators验证器中添加验证函数即可,例如:将name = StringField('name', validators=[DataRequired()])代码改为:
name = StringField('name', validators=[DataRequired(),length(min=2,max=6)])
表单类文件已经写好了,接着在templates目录下创建一个名为form的html文件,然后在Flask项目的app.py文件中编写视图函数,代码如下所示:
from flask import Flask, render_template
from form import MyForm
app = Flask(__name__)
@app.route('/')
def user_form():
myform=MyForm() #创建表单类对象
if myform.validate_on_submit():
return '提交成功'
return render_template('form.html',myform=myform) #渲染form.html,并将myform表单对象传到form.html中
if __name__ == '__main__':
app.run()
导入必要的包与库,在视图函数中创建刚才编写的表单类对象myform,validate_on_submit()方法检查是否是一个POST请求并且请求是否有效,再通过render_template()方法渲染form.html文件,并将myform传入form.html中。
app.py文件中的视图函数已经写好了,接下来编写刚才创建的form.html文件,其文件代码如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{#创建表单#}
<form action="" method="post">
<p>{{ myform.name }}</p>
<p>{{ myform.password }}</p>
<p>{{ myform.submit }}</p>
</form>
</body>
</html>
在form.html文件中,我们通过{{ 表单类对象.属性 }}的方式渲染表单字段,当我们表单类字段定义中传入label参数时,还可以在模板文件中通过{{ 表单类对象.属性.label }}渲染该字段的label文本。
是不是这样就可以了呢,我们运行Flask项目,浏览器访问http://127.0.0.1:5000/,发现报了如下错误:
RuntimeError: A secret key is required to use CSRF. #运行时错误:使用CSRF需要密钥
这时我们只需要在配置中添加SECRET_KEY即可,代码如下所示:
app.config['SECRET_KEY']='hakhfaskh' #SECRET_KEY值是任意的
重新启动Flask项目并浏览http://127.0.0.1:5000/就不会报错了,如下图所示:
当我们没输入信息并按提交时,系统会自动提示浏览器内置的错误提示。
观察其源码,可以发现可以发现WTForm表单字段的第一个参数为表单的id和name的值。
自定义验证
一般来说,如果对表单有额外需要的验证,一般自定义表单的额外的验证方法而不是重新自定义新的字段,我们可以通过form.py表单类中使用validate_<fieldname>自定义验证,示例代码如下所示:
def validate_name(self,data): #为name添加自定义validata_%s,并传入输入值data
if self.name.data[0].isdigit(): #使用isdigit检验是否为数字开头,使用data[0]获取数据的首位
raise ValidationError('用户名不能以数字开头') #若验证不通过则抛出异常
这里我们为form.py表单类中的name添加自定义验证,所以自定义函数名为validate_name,如果我们为表单类中的password添加自定义验证时,自定义函数名为validate_password,也就是说自定义验证函数名要和表单类中字段名要对应。
这里我们为用户名添加了首字母不能以数字开头的验证,启动flask项目,并访问http://127.0.0.1:5000/,如下图所示:
CSRF保护
在上面的操作中,还没使用CSRF保护,在使用CSRF保护前,我们先了解一下CSRF的一些知识。
CSRF:跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击一样,存在巨大的危害性。
简单来说就是黑客盗用了你的身份,以你的名义发送恶意请求,服务器不知道是黑客请求的,服务器认为是你本人发出的请求。
其攻击原理如下图所示:
用户小明通过输入用户名、密码登录某安全网站A,网站服务器通过验证信息返回cookie给用户的浏览器,在未退出网站A或删除cookie期间,小明浏览了某不良网站B,不良网站B攻击代码来获取你安全网站A的cookie值并访问网站A,由于黑客拥有小明网站A的cookie,就可以以小明的身份访问网站A,以小明的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等操作。
很多网络诈骗是CSRF攻击,所以大家不要随便点击不明来路的链接和不要浏览不良网站。
那么如何防御CSRF攻击呢?
在Flask项目中,使用CSRFProtect()方法启动全局启用CSRF保护,代码如下所示:
from flask import Flask, render_template
from flask_wtf import CSRFProtect
from form import MyForm
app = Flask(__name__)
app.config['SECRET_KEY']='hakhfaskh'
#启动CSRF保护
csrf = CSRFProtect(app)
@app.route('/',methods=['GET','POST'])
def hello_world():
myform=MyForm() #创建表单类对象
return render_template('form.html',myform=myform)
if __name__ == '__main__':
app.run()
CSRF保护需要一个密钥,CSRF默认是使用配置的SECRET_KEY值,当然我们还可以单独使用WTF_CSRF_SECRET_KEY来设置。
在HTML文件中,我们需要渲染csrf_token或使用<input type="hidden"来隐藏csrf_token代码如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{{ myform.csrf_token }} {#渲染csrf_token#}
{# 使用<input type="hidden"隐藏csrf_token#}
{# <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>#}
<p>{{ myform.name }}</p>
<p>{{ myform.password }}</p>
<p>{{ myform.submit }}</p>
</form>
</body>
</html>
这样就成功使用了CSRF保护,启动Flask项目并访问http://127.0.0.1:5000/,如下图所示:
当我们在网站中表单中填写信息会携带隐藏值提交给服务器,服务器根据隐藏值和用户填写的信息返回cookie值给用户,而cookie值没有隐藏值的信息,即使黑客获取到我们的cookie值,但无法获取隐藏值,没有隐藏值就无法得到服务器的信息验证。从而实现了CSRF保护、数据安全。
好了,关于Flask框架——Flask-WTF表单:数据验证、CSRF保护就讲到这里了,感谢观看,下篇文章学习Flask框架——Flask-WTF表单:文件上传、验证码。
公众号:白巧克力LIN
该公众号发布Python、数据库、Linux、Flask、自动化测试、Git等相关文章!