4.1 跨站请求伪造保护
跨站请求伪造(CSRF):恶意网站吧请求发送到,被攻击者已登录的其他网站。flask-wtf
需要程序设置一个密匙。然后利用这个密匙生成加密令牌,再用令牌验证请求中表单数据的真伪,从而实现CSRF保护。
设置flask-wtf
:
注意:为了增强安全性,密匙不应该直接写入代码中,而要保存环境变量中(第七章介绍)。
4.2 定义表单类
使用flask-wtf
时,每个Web表单都继承自Form
类。这个类定义表单中的一组字段,每组字段都用对象(字段类的实例对象,如StringField(‘hello’)
)表示。字段对象可以附有一个或多个验证函数。
验证函数(validator):用来验证用户提交的数据是否符合要求。
定义名为NameForm的表单类:
NameForm
表单中的字段定义为类变量,
类变量的值为对应字段的对象。
字段构造函数(如SubmitField()):的第一个参数是把表单渲染成HTML时使用的标号(或文本)。
validators
参数:一个由验证函数组成的列表。
表4-1 WTFORMS支持的HTML标准字段
字段类型 | 说明 |
---|---|
StringField | 文本字段 |
TextAreaField | 多行文本字段 |
PasswordField | 密码文本字段 |
表4-2 WTFORMS验证函数
验证函数 | 说明 |
---|---|
验证电子邮件地址 | |
Required | 确保字段中有数据 |
AnyOf | 确保输入值在可选值列表中 |
4.3 把表单渲染成HTML
假设视图函数把一个NameForm
实例(通过参数form
)传给模板,那么在模板中可以生成一个简单的表单:
为了改进外观,可以将HTML属性(如id、class等)传给渲染字段的函数,如:
flask-bootstrap
提供了一个辅助函数涌来渲染flask-wtf
表单。用法如下:
bootstrap/wtf.html
文件中定义了一个用于渲染Flask-WTF表单对象的辅助函数。
wtf.qucik_form()
函数的参数为Flask-WTF表单对象(表单类的实例)。
4.4 在视图函数中处理表单
|
|
methods
参数告诉Flask在URL映射中把这个视图函数注册为GET
和POST
请求的处理程序。如果没有指定methods
参数,就只把视图函数注册为GET请求的处理程序。
validate_on_submit()
:提交表单后,如果数据能被全部验证函数接受,那么validate_on_submit()
的返回值为True
,否则返回False
。
4.5 重定向和用户会话
现在的hello.py存在一个问题:用户输入名字后提交表单,然后刷新页面,会出现一个警告(要求在再次提交表单前进行确认)。
出现这种情况的原因是:刷新页面时,浏览器会重新发送之前已经发送过的最后一个请求。如果这个请求是一个包含表单数据的POST请求,刷新页面会再次提交表单。
解决思路:使浏览器发送的最后一个请求不是POST请求。
解决方法:使用重定向作为POST请求的响应,而不是使用常规响应。
重定向:是一个特殊响应,响应的内容是URL,而不是包含HTML代码的字符串。浏览器收到这种响应时,会向重定向的URL发起GET请求(第二个请求),显示页面内容。
存在问题:这种方法会带来另一个问题,程序处理POST请求时,使用form.name.data
来获取用户输入值,可是一旦这个请求结束,数据也就丢失(因为这个POST请求使用重定向作为响应)。所以程序需要保存输入值,方便重定向后的请求可以获得并使用这个值。
解决方法2 :把数据存储在用户会话中。
使用重定向和用户会话重写hello.py
:
使用session.get()
方法获取字典中键对应的值,避免未找到键的异常情况。因为对于不存在的键, get()
会返回默认值None
。
4.6 Flash消息
请求完成后,有时需要让用户知道状态发生了变化(如使用确认消息、警告或错误提醒)。一个典型例子是,提交了一项有错误的登录表单后,服务器发回的响应重新渲染登录表单,并且在表单上面显示一个消息,提示用户名或密码错误。
设置Flash消息的hello.py
:
在这个示例中,将用户提交的数据和用户会话中的数据比较,如果两个数据不一样,就会调用flash()
函数,在发给客户端的下一个响应中显示一个信息。
在模板中渲染Flash消息:仅调用flash()
函数并不能将消息显示出来,这个可以在模板中渲染Flash消息。如base.html
:
get_flashed_messages()
函数用来获取并渲染Flash消息。
注意:
- 在模板中使用
for
循环是因为,每次调用falsh()
函数时,都会生产一个消息,所以可能有多个消息在排队等待显示,所需需要用for
循环将消息都显示出来。 get_flashed_messages()
函数获取的消息在下次调用时不会再次返回(如这次获取了消息队列1,那么在下次调用时就不会再返回消息队列1,有点类似于列表的pop()
方法),因此Flash消息只显示一次,然后就消失了。