Flask 框架 - 模板 - 2
1 学习目标
- 能够说出Flask中模板代码复用的三种方式
- 能够使用代码实现模板继承的功能
- 能够说出可以在模板中直接使用的 Flask 变量和函数
- 能够使用 Flask-WTF 扩展实现注册表单
- 能够说出 CSRF 攻击的原理
2 模板代码复用
在模板中,可能会遇到以下情况:
- 多个模板具有完全相同的顶部和底部内容
- 多个模板中具有相同的模板代码内容,但是内容中部分值不一样
- 多个模板中具有完全相同的 html 代码块内容
像遇到这种情况,可以使用 JinJa2 模板中的宏、继承、包含来进行实现
2.1 宏
对宏(macro)的理解:
- 把它看作 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串
- 为了避免反复地编写同样的模板代码,出现代码冗余,可以把他们写成函数以进行重用
- 需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复
2.1.1 使用
- 定义宏
{% macro input(name,value='',type='text') %}<input type="{{ type }}" name="{{ name }}" value="{{ value }}" class="form-control">
{% endmacro %}
- 调用宏
{{ input('name' value='zs') }}
- 这会输出
<input type="text" name="name" value="zs" class="form-control">
- 把宏单独抽取出来,封装成html文件,其它模板中导入使用,文件名可以自定义macro.html
{% macro function(type='text', name='', value='') %}<input type="{{type}}" name="{{name}}" value="{{value}}" class="form-control">
{% endmacro %}
- 在其它模板文件中先导入,再调用
{% import 'macro.html' as func %}
{% func.function() %}
2.1.2 代码演练
- 使用宏之前代码
<form><label>用户名:</label><input type="text" name="username"><br/><label>身份证号:</label><input type="text" name="idcard"><br/><label>密码:</label><input type="password" name="password"><br/><label>确认密码:</label><input type="password" name="password2"><br/><input type="submit" value="注册">
</form>
- 定义宏
{# 定义宏,相当于定义一个函数,在使用的时候直接调用该宏,传入不同的参数就可以了 #}
{% macro input(label="", type="text", name="", value="") %}<label>{{ label }}</label><input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
- 使用宏
<form>{{ input("用户名:", name="username") }}<br/>{{ input("身份证号:", name="idcard") }}<br/>{{ input("密码:", type="password", name="password") }}<br/>{{ input("确认密码:", type="password", name="password2") }}<br/>{{ input(type="submit", value="注册") }}
</form>
- 结果展示:
注:也可以将宏定义到外部的html中,导入后再使用(如下图)
2.2 模板继承
模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。
- 标签定义的内容
{% block content %}内容
{% endblock content %}
- 相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。
- 子模板使用 extends 指令声明这个模板继承自哪个模板
- 父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()
2.2.1 父模板
base.html
{% block top %}顶部内容
{% endblock top %}{% block content %}父类中间内容
{% endblock content %}{% block bottom %}底部内容
{% endblock bottom %}
2.2.2 子模板
extends 指令声明这个模板继承自哪
{% extends 'base.html' %}
{% block content %}子类中间需要填充的内容
{% endblock content %}
模板继承使用时注意点:
- 不支持多继承
- 为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。
- 不能在一个模板文件中定义多个相同名字的block标签。
- 当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。
那如果子模板中还想使用父模板坑中内容怎么办? 如下:调用super()
是否调用super() 总结:
如果是子模板要重写父模板的坑,那就不调用
如果是子模板要在父模板的基础上增加内容,那就调用
2.3 继承模板抽取演练
2.3.1 demo准备
- 我们先准备一个demo,将现成的一个案例拿过来
- 访问:注意这是静态文件,可以直接访问的
- 接下来我们就需要将静态html变为模板文件了:
分析之前的俩界面的内容:有头和脚是相同的,但是也有些不同的,比如详情的头是没有标题的
2.3.2 抽取
接下来我们来抽取:我们将 index 的所有内容copy到 news_base.html 中 (注意:这里也可以将 detail 的所有内容copy到news_base.html中)
然后分析不同之处: 将不同之处在base中变为block。
发现网页标题不一样:
发现有js不一样:
挖坑:
然后将剩下内容都折叠起来对比一下,发现结构都是一样的:
然后主要是中间的内容不一样:
将base中的这个ul干掉,变为坑:
接下来让 index 继承 base:
其俩坑好填:
主要来看一下内容这个坑:
处理后的结果如下:
接下来增加视图函数,访问:
访问:
处理 detial 和 处理 index 逻辑一样,就不在赘述,请读者自己考虑。
最终结果如下:
友情提示:这里不要着急把源代码删了,后面可能还会用到
注意这里的区别:conter_con里的内容是 一个div
访问:(发现问题:多了一个标题,少了一个发布者)
少的发布者效果如下:
2.3.3 解决问题
先来处理多出来的头的标题:
这个是index独有的,那就不应该放在base中:
处理如下:
注意:将这个选中内容剪切出去,base中不能有
index最终如下:
访问detail: (发现没有头的标题了)
但是还少一个发布者,这个发布者是detail独有的,也不能在base中,所以base需要留一个坑,先找到这个发布者效果的代码如下:
base留坑:
将发布者的代码,放到detail的坑中:
访问:没问题了
首页也没有问题:
2.4 包含
2.4.1 简介
Jinja2模板中,除了宏和继承,还支持一种代码重用的功能叫包含(Include)。它的功能是将另一个模板整个加载到当前模板中,并直接渲染。
- include的使用
{% include 'hello.html' %}
包含在使用时,如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上 ignore missing
关键字。如果包含的模板文件不存在,会忽略这条include语句。
- include 的使用加上关键字 ignore missing
{% include 'hello.html' ignore missing %}
2.4.2 代码演练
- 新建函数:
from flask import Flask, render_templateapp = Flask(__name__)@app.route("/")
def index():return "index"@app.route("/include")
def include():return render_template("demo5_include.html")if __name__ == '__main__':app.run(debug=True)
新建include模板(这个模板是要被别人包含)文件名为:included.html,代码如下:
<h3>拼个天翻地覆,搏个无怨无悔</h3>
新建模板,并使用包含模板,代码如下:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
hello, world!<br/>
{% include "included.html" %}<br/>
{% include "included.html" %}<br/>
{% include "included.html" %}<br/>
</body>
</html>
小技巧: 光标放在相应行,Ctrl + d 复制本行,Ctrl + y 删除本行
- 结果如下:
- 如果写错了,就会报错:
{% include "included22222.html" %}<br/>
- 可以忽略这个错误:
ignore missing:忽略丢失,刚刚的错误其实就是找不到的错误
效果如下:
2.4 小结
- 宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。
- 继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。
- 宏(Macro)的功能类似函数,可以传入参数,需要定义、调用。
- 包含(include)是直接将目标模板文件整个渲染出来。
3 模板中特有的变量和函数
你可以在自己的模板中访问一些 Flask 默认内置的函数和对象
- config
你可以从模板中直接访问Flask当前的config对象:
{{ config.DEBUG }}
- request
就是flask中代表当前请求的request对象:
{{ request.url }}
- session
为Flask的session对象
{{ session.name }}
- g变量
在视图函数中设置g变量的 name 属性的值,然后在模板中直接可以取出
{{ g.name }}
- url_for()
url_for会根据传入的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就可以安全的修改路由绑定的URL,则不比担心模板中渲染出错的链接:
{{ url_for('index') }}
如果我们定义的路由URL是带有参数的,则可以把它们作为关键字参数传入url_for(),Flask会把他们填充进最终生成的URL中:
{{ url_for('post', post_id=1) }}
- get_flashed_messages()
这个函数会返回之前在flask中通过flask()传入的消息的列表,flash函数的作用很简单,可以把由Python字符串表示的消息加入一个消息队列中,再使用get_flashed_message()函数取出它们并消费掉:
{% for message in get_flashed_messages() %}{{ message }}
{% endfor %}
完整代码如下:
# demo6_special.py
from flask import Flask, render_template, session, g, flashapp = Flask(__name__)
app.secret_key = "apollo_miracle"@app.route("/")
def index():return "index"@app.route("/demo")
def demo():session.name = "apollo"g.name = "miracle"flash("我是闪现信息")return render_template("demo6_special.html")if __name__ == '__main__':app.run(debug=True)
{# demo6_special.html #}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>config.DEBUG --> {{ config.DEBUG }}<br/>request.url --> {{ request.url }}<br/>session.name --> {{ session.name }}<br/>g.name --> {{ g.name }}<br/>url_for('index') --> {{ url_for('index') }}<br/>
<a href="{{ url_for("index") }}">返回首页</a><br/>闪现信息 --> {% for message in get_flashed_messages() %}{{ message }}<br/>
{% endfor %}</body>
</html>
效果如下:
4 Web表单
Web 表单是 Web 应用程序的基本功能。
它是HTML页面中负责数据采集的部件。表单有三个部分组成:表单标签、表单域、表单按钮。表单允许用户输入数据,负责HTML页面数据采集,通过表单将用户输入的数据提交给服务器。
在Flask中,为了处理web表单,我们可以使用 Flask-WTF 扩展,它封装了 WTForms,并且它有验证表单数据的功能
4.1 WTForms支持的HTML标准字段
字段对象 | 说明 |
---|---|
StringField | 文本字段 |
TextAreaField | 多行文本字段 |
PasswordField | 密码文本字段 |
HiddenField | 隐藏文件字段 |
DateField | 文本字段,值为 datetime.date 文本格式 |
DateTimeField | 文本字段,值为 datetime.datetime 文本格式 |
IntegerField | 文本字段,值为整数 |
DecimalField | 文本字段,值为decimal.Decimal |
FloatField | 文本字段,值为浮点数 |
BooleanField | 复选框,值为True 和 False |
RadioField | 一组单选框 |
SelectField | 下拉列表 |
SelectMutipleField | 下拉列表,可选择多个值 |
FileField | 文件上传字段 |
SubmitField | 表单提交按钮 |
FormField | 把表单作为字段嵌入另一个表单 |
FieldList | 一组指定类型的字段 |
4.2 WTForms常用验证函数
验证函数 | 说明 |
---|---|
DataRequired | 确保字段中有数据 |
EqualTo | 比较两个字段的值,常用于比较两次密码输入 |
Length | 验证输入的字符串长度 |
NumberRange | 验证输入的值在数字范围内 |
URL | 验证URL |
AnyOf | 验证输入值在可选列表中 |
NoneOf | 验证输入值不在可选列表中 |
使用 Flask-WTF 需要配置参数 SECRET_KEY。
CSRF_ENABLED是为了CSRF(跨站请求伪造)保护。 SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置会根据设置的密匙生成加密令牌。
4.3 代码验证
4.3.1 使用 html 自带的表单
- 创建模板文件demo7_wtf.html ,在其中直接写form表单:
<form method="post"><label>用户名:</label><input type="text" name="username" placeholder="请输入用户名"><br/><label>密码:</label><input type="password" name="password" placeholder="请输入密码"><br/><label>确认密码:</label><input type="password" name="password2" placeholder="请输入确认密码"><br/><input type="submit" value="注册">
</form>{% for message in get_flashed_messages() %}{{ message }}
{% endfor %}
- 视图函数中获取表单数据验证登录逻辑:(之前元素的表单验证,是自己写验证逻辑)
@app.route("/register", methods=["GET", "POST"])
def register():if request.method == "POST":username = request.form.get("username")password = request.form.get("password")password2 = request.form.get("password2")# 判断三个参数是否全部填写if not all([username, password, password2]):flash("参数错误")# 判断两次密码是否一致elif password != password2:flash("两次密码不一致")else:print(username, password, password2)return "success"# 渲染return render_template("demo7_wtf.html")
4.3.2 使用 Flask-WTF 实现表单
- 使用wtf来实现表单
# 自定义注册表单
class RegisterForm(FlaskForm):username = StringField("用户名:", validators=[InputRequired("请输入用户名")], render_kw={"placeholder": "我是占位文字"})password = PasswordField("密码:", validators=[InputRequired("请输入密码")])password2 = PasswordField("确认密码:", validators=[InputRequired("请输入确认密码"), EqualTo("password", "两次密码要一致")])submit = SubmitField("注册")
代码说明:
validators:调用`validate`时调用的一系列验证器
InputRequired(“请输入用户名”):是必填的意思。如果用户没有填写内容,就提示”请输入用户名”
EqualTo是做相等判断(判断 password 与 password2 是否相等),如果不相等,就提示”两次密码要一致”
render_kw:如果提供,则提供默认关键字的字典,将在渲染时提供给窗口小部件
placeholder:占位文字
- 进行表单验证
@app.route("/register_wtf", methods=["GET", "POST"])
def register_wtf():register_form = RegisterForm()# 使用 wtf表单帮我们做验证if register_form.validate_on_submit():# 执行注册逻辑# 取到表单中提交上来的三个参数# 取值方式1username = request.form.get("username")password = request.form.get("password")password2 = request.form.get("password2")# 取值方式2# username = register_form.username.data# 假装做注册操作print(username, password, password2)return "success"else:if request.method == "POST":flash('参数错误')return render_template("demo7_wtf.html", form=register_form)
代码说明:
代码中有一个validate_on_submit()意思是点击注册(submit)按钮的时候会校验,校验规则如上(代码说明)
- 访问时,两次密码输入一致,点击注册,断点调试如下:
- 发现没有严重通过,而是有错误:
这个错误是确实csrftoken , 这个目前我们不处理
先做如下设置:
- 配置参数,关闭 CSRF 校验
app.config['WTF_CSRF_ENABLED'] = False
- 再次访问即没问题。
CSRF:跨站请求伪造,后续会讲到
- 模板页面:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><form method="post"><label>用户名:</label><input type="text" name="username" placeholder="请输入用户名"><br/><label>密码:</label><input type="password" name="password" placeholder="请输入密码"><br/><label>确认密码:</label><input type="password" name="password2" placeholder="请输入确认密码"><br/><input type="submit" value="注册">
</form>{# get_flashed_messages() 是函数,不能忘记后边的括号 #}
{% for message in get_flashed_messages() %}{{ message }}
{% endfor %}<hr>
<h3>以下表单通过 WTF 来实现:</h3><br/><form method="post">{{ form.username.label }}{{ form.username }}<br/>{{ form.password.label }}{{ form.password }}<br/>{{ form.password2.label }}{{ form.password2 }}<br/>{{ form.submit }}
</form></body>
</html>
- 视图函数:
from flask import Flask, render_template, request, flash
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import InputRequired, EqualToapp = Flask(__name__)
# 关闭csrf验证
app.config['WTF_CSRF_ENABLED'] = False
app.secret_key = "apollo_miracle"# 自定义注册表单
class RegisterForm(FlaskForm):username = StringField("用户名:", validators=[InputRequired("请输入用户名")], render_kw={"placeholder": "我是占位文字"})password = PasswordField("密码:", validators=[InputRequired("请输入密码")])password2 = PasswordField("确认密码:", validators=[InputRequired("请输入确认密码"), EqualTo("password", "两次密码要一致")])submit = SubmitField("注册")@app.route("/")
def index():return "index"@app.route("/register", methods=["GET", "POST"])
def register():if request.method == "POST":username = request.form.get("username")password = request.form.get("password")password2 = request.form.get("password2")# 判断三个参数是否全部填写if not all([username, password, password2]):flash("参数错误")# 判断两次密码是否一致elif password != password2:flash("两次密码不一致")else:print(username, password, password2)return "success"# 渲染return render_template("demo7_wtf.html")@app.route("/register_wtf", methods=["GET", "POST"])
def register_wtf():register_form = RegisterForm()# 使用 wtf表单帮我们做验证if register_form.validate_on_submit():# 执行注册逻辑# 取到表单中提交上来的三个参数# 取值方式1username = request.form.get("username")password = request.form.get("password")password2 = request.form.get("password2")# 取值方式2# username = register_form.username.data# 假装做注册操作print(username, password, password2)return "success"else:if request.method == "POST":flash('参数错误')return render_template("demo7_wtf.html", form=register_form)if __name__ == '__main__':app.run(debug=True)
- 测试结果:
5 CSRF 原理分析
5.1 CSRF 简介
CSRF
全拼为Cross Site Request Forgery
,译为跨站请求伪造。
CSRF
指攻击者盗用了你的身份,以你的名义发送恶意请求。
- 包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......
造成的问题:个人隐私泄露以及财产安全。
5.2 CSRF攻击示意图
客户端访问服务器时没有同服务器做安全验证
5.3 防止 CSRF 攻击
5.3.1 步骤
- 在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值
- 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token
- 在用户点击提交的时候,会带上这两个值向后台发起请求
- 后端接受到请求,以会以下几件事件:
- 从 cookie中取出 csrf_token
- 从表单数据中取出来隐藏的 csrf_token 的值
- 进行对比
- 如果比较之后两值一样,那么代表是正常的请求,如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作
5.3.2 未进行 csrf 校验的网站代码演示
未进行 csrf 校验的 WebA
- 后端代码实现
from flask import Flask, render_template, make_response
from flask import redirect
from flask import request
from flask import url_forapp = Flask(__name__)@app.route('/', methods=["POST", "GET"])
def index():if request.method == "POST":# 取到表单中提交上来的参数username = request.form.get("username")password = request.form.get("password")if not all([username, password]):print('参数错误')else:print(username, password)if username == 'laowang' and password == '1234':# 状态保持,设置用户名到cookie中表示登录成功response = redirect(url_for('transfer'))response.set_cookie('username', username)return responseelse:print('密码错误')return render_template('temp_login.html')@app.route('/transfer', methods=["POST", "GET"])
def transfer():# 从cookie中取到用户名username = request.cookies.get('username', None)# 如果没有取到,代表没有登录if not username:return redirect(url_for('index'))if request.method == "POST":to_account = request.form.get("to_account")money = request.form.get("money")print('假装执行转操作,将当前登录用户的钱转账到指定账户')return '转账 %s 元到 %s 成功' % (money, to_account)# 渲染转换页面response = make_response(render_template('temp_transfer.html'))return responseif __name__ == '__main__':app.run(debug=True, port=9000)
- 前端登录页面代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录</title>
</head>
<body><h1>我是网站A,登录页面</h1><form method="post"><label>用户名:</label><input type="text" name="username" placeholder="请输入用户名"><br/><label>密码:</label><input type="password" name="password" placeholder="请输入密码"><br/><input type="submit" value="登录">
</form></body>
</html>
- 前端转账页面代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>转账</title>
</head>
<body>
<h1>我是网站A,转账页面</h1><form method="post"><label>账户:</label><input type="text" name="to_account" placeholder="请输入要转账的账户"><br/><label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/><input type="submit" value="转账">
</form></body>
</html>
运行测试,如果在未登录的情况下,不能直接进入转账页面,测试转账是成功的
伪造逻辑(csrf发生过程)
首先,他先来了解了一下你的转账界面:
知道了你转账的请求的url,就是当前界面,转账所带的参数有to_account和money,接下来他写了如下攻击程序,demo代码如下:
代码核心:
这里也有一个表单,里边请求的 action 是转账的网址,参数、请求方式、啥的都一样,但是to_account就是黑客自己写的了,金钱也是黑客自己写的(意思就是黑客想给谁转多少钱都可以了)
攻击网站B的代码
- 后端代码实现
from flask import Flask
from flask import render_templateapp = Flask(__name__)@app.route('/')
def index():return render_template('temp_index.html')if __name__ == '__main__':app.run(debug=True, port=8000)
- 前端代码实现
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>我是网站B</h1><form method="post" action="http://127.0.0.1:9000/transfer"><input type="hidden" name="to_account" value="999999"><input type="hidden" name="money" value="190000" hidden><input type="submit" value="点击领取优惠券">
</form></body>
</html>
运行测试,在用户登录网站A的情况下,点击网站B的按钮,可以实现伪造访问
访问网站B:
点击领取优惠劵按钮:
发现竟然转账成功了(因为我们转账的网站,已经在当前浏览器登录,已经有cookie了)
5.3.3 在网站A中模拟实现 csrf_token 校验的流程
- 添加生成 csrf_token 的函数
# 生成 csrf_token 函数
def generate_csrf():return bytes.decode(base64.b64encode(os.urandom(48)))
在渲染转账页面的,做以下几件事情:
- 生成 csrf_token 的值
- 在返回转账页面的响应里面设置 csrf_token 到 cookie 中
- 将 csrf_token 保存到表单的隐藏字段中
@app.route('/transfer', methods=["POST", "GET"])
def transfer():...# 生成 csrf_token 的值csrf_token = generate_csrf()# 渲染转换页面,传入 csrf_token 到模板中response = make_response(render_template('temp_transfer.html', csrf_token=csrf_token))# 设置csrf_token到cookie中,用于提交校验response.set_cookie('csrf_token', csrf_token)return response
- 在转账模板表单中添加 csrf_token 隐藏字段
<form method="post"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><label>账户:</label><input type="text" name="to_account" placeholder="请输入要转账的账户"><br/><label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/><input type="submit" value="转账">
</form>
- 运行测试,进入到转账页面之后,查看 cookie 和 html 源代码
多次刷新,发现这个csrf_token是变化的
- 在执行转账逻辑之前进行 csrf_token 的校验
if request.method == "POST":to_account = request.form.get("to_account")money = request.form.get("money")# 取出表单中的 csrf_tokenform_csrf_token = request.form.get("csrf_token")# 取出 cookie 中的 csrf_tokencookie_csrf_token = request.cookies.get("csrf_token")# 进行对比if cookie_csrf_token != form_csrf_token:return 'token校验失败,可能是非法操作'print('假装执行转操作,将当前登录用户的钱转账到指定账户')return '转账 %s 元到 %s 成功' % (money, to_account)
运行测试,用户直接在网站 A 操作没有问题,再去网站B进行操作,发现转账不成功,因为网站 B 获取不到表单中的 csrf_token 的隐藏字段,而且浏览器有同源策略,网站B是获取不到网站A的 cookie 的,所以就解决了跨站请求伪造的问题
5.3.4 csrf 校验的网站代码再度优化
- 打开网站A页面,登录:
- 进行转账:
- 转账之后进行刷新:
- 每刷新一次,就会“转账 100 元到 6666666666 成功”一次,what????
代码改进:重新生成 csrf_token 或者 转账成功之后跳转页面
- 重新生成 csrf_token 代码如下:
- 再次刷新:
- 搞定。。。
6 在 Flask 项目中解决 CSRF 攻击
在 Flask 中, Flask-wtf 扩展有一套完善的 csrf 防护体系,对于我们开发者来说,使用起来非常简单
首先打开csrf验证:
app.config['WTF_CSRF_ENABLED'] = True
在表单中添加隐藏域:csrf_token:
运行,访问后查看源码:
并且查看cookie:
发现wtf将csrf存储到了session中,所以这里的这个cookie中存储的其实就相当于是一个session_id,然后需要根据这个session_id 去session 中取出对应的 scrf 然后进行校验。
你会发现这俩值不一样.
因为一个是真正的csrf_token值,一个是csrf_token对应的一个id。
单独使用
- 设置应用程序的 secret_key
- 用于加密生成的 csrf_token 的值
app.secret_key = "#此处可以写随机字符串#"
- 导入 flask_wtf.csrf 中的 CSRFProtect 类,进行初始化,并在初始化的时候关联 app
from flask.ext.wtf import CSRFProtect
CSRFProtect(app)
- 如果模板中有表单,不需要做任何事。与之前一样:
<form method="post">{{ form.csrf_token }}...
</form>
- 但如果模板中没有表单,你仍需要 CSRF 令牌:
<form method="post" action="/"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
后续项目中会使用到此功能
Flask 框架 - 模板 - 2相关推荐
- flask html 模板继承,Flask框架模板继承实现方法分析
Flask框架模板继承实现方法分析 本文实例讲述了Flask框架模板继承实现方法.分享给大家供大家参考,具体如下: 在模板中,可能会遇到以下情况: 多个模板具有完全相同的顶部和底部内容 多个模板中具有 ...
- Flask框架——模板、数据库ORM
文章目录 1 模板 1 重定向 1.1 什么是重定向? 1.2 为什么要有重定向? 1.3 如何使用重定向? 1.3.1 暂时性重定向(代码实例): 1.3.2 永久性重定向(代码实例) 2 jinj ...
- flask mysql项目模板渲染_Flask框架模板渲染操作简单示例
本文实例讲述了Flask框架模板渲染操作.分享给大家供大家参考,具体如下: from flask import render_template from flask import Flask from ...
- flask框架中的Jinja2模板引擎
简介 在flask框架中通常使用Jinja2模板引擎来实现复杂页面的渲染. 本章主要介绍Jinja2模板引擎的基本结构和使用方法. 如何使用flask框架渲染模板 在模板中传递一个或者多个参数 if语 ...
- Flask框架——蓝图
在上篇文章中,我们学习了Flask框架--模板复用(继承.包含.宏),这篇文章我们来学习Flask框架--蓝图. 随着Flask项目越来越复杂,把所有视图函数放在一个应用文件中会很不方便我们管理,所以 ...
- Flask框架之模板继承与案例05
Flask框架之模板继承与案例05 一,模版继承 二,静态文件的配置 1.这里列举一个小例子: 三,模版案例(制作豆瓣评分页面) 1.效果图: 2.数据准备: 3.代码链接 一,模版继承 Flask中 ...
- Flask框架基础Jinja2模板
Flask框架基础Jinja2模板-- 潘登同学的flask学习笔记 文章目录 Flask框架基础Jinja2模板-- 潘登同学的flask学习笔记 return 模板 Template 模板的使用 ...
- flask 使用html模板,Flask框架使用HTML模板的方法
Flask框架使用HTML模板的方法 发布时间:2020-08-13 13:40:00 来源:亿速云 阅读:111 作者:小新 这篇文章主要介绍了Flask框架使用HTML模板的方法,具有一定借鉴价值 ...
- Flask框架 请求与响应 模板语法
目录 Flask框架 请求与响应 & 模板语法 简单了解Flask框架 Flask 框架 与 Django 框架对比 简单使用Flask提供服务 Flask 中的 Response(响应) F ...
最新文章
- Linux -- 利用IPS(***防御系统) 构建企业Web安全防护网
- java 查询表 并返回数据_ajax与java前后台传值及数据表查询解决一个bug的问题
- 我作为开发者犯过的两次愚蠢的错误
- 16岁成为全栈开发者:我从开发游戏到写加密货币投资机器人的心路历程
- python列表间隔合并_Python使用zip合并相邻列表项的方法示例
- 位置子段最大子段和 hdu 1003 max sum ACM的开始
- Github issue里MVP和Stretch label的区别
- java打印前线程的id_logback打印日志输出线程ID:切面模式
- 【LeetCode】剑指 Offer 40. 最小的k个数
- PostMan入门使用教程
- 8-18 高可用读写分离
- 在android 采用 android junit test 测试注意
- idea中Gsonformat插件工具使用
- oracle自动导出awr报告,自动生成awr报告脚本
- 虚拟机ip、主机ip、代理ip
- 安装neo4j过程中存在的问题
- SaaS服务应用集成和生态该何去何从
- Win10上轻松安装docker,更改镜像安装目录
- 2048php,Phaser实现2048
- 十万个seo_2020年将抛弃10个SEO神话
热门文章
- 视频调色软件有哪些?视频调色软件推荐。
- noc大赛初赛python试题-小学组
- 在线时间戳转换工具的坑-同样的时间戳转为北京时间,转换结果受本机时区设置的影响...
- 开源赋能 普惠未来|中软国际寄语 2023 开放原子全球开源峰会
- iPhone4s美版港版国行区别!iphone4s哪个版本好?iphone4s版本区别!港行iphone4s siri!香港价格美国价格...
- 如何用最短的时间学会数据分析?
- oppovivo会用鸿蒙系统么,华为已经做好准备,就等小米、OPPO、vivo了!
- 扫盲人工智能的计算力基石--异构计算
- echarts+bmap
- Axure RP 8的中继器