前言:flask_wtf本身提供了生成表单HTML页面的功能(基于wtforms提供),常用于开发前后端不分离的表单页面,同时Flask-wtf 扩展模块还提供了一套完善的 csrf 防护体系,对于我们开发者来说,使用flask_wtf模块就可以非常简单解决CSRF攻击问题

CSRF: 跨域请求伪造攻击。

pip install flask_wtf

5.1 图示

image

5.2 服务端配置

  1. 设置应用程序的 secret_key,用于加密生成的 csrf_token 的值
# 1. session加密的时候已经配置过了.如果没有在配置项中设置,则如下:
app.secret_key = "#此处可以写随机字符串#"

# 2. 也可以写在配置类中。
class Config(object):
    DEBUG = True
    SECRET_KEY = "dsad32DASSLD*13%^32"
    
"""加载配置"""
app.config.from_object(Config)
  1. 导入 flask_wtf 中的 CSRFProtect类,进行初始化,并在初始化的时候关联 app
# 方式1:
from flask_wtf import CSRFProtect
csrf = CSRFProtect() # 这块代码可能在文件中。
app = Flask(import_name=__name__, template_folder="templates")
# 项目配置代码之后
csrf.init_app(app) # 避免出现引用导包,所以提供了init_app的用法

# 方式2:
# from flask_wtf import CSRFProtect
# app = Flask(import_name=__name__, template_folder="templates")
# 项目配置代码之后
# CSRFProtect(app)

视图代码:

from flask import Flask, render_template, request, session
from flask_wtf import CSRFProtect
from flask_wtf.csrf import generate_csrf
app = Flask(__name__, template_folder="templates", static_folder="static")

# csrf = CSRFProtect()



# 配置
app.config.update({
    "DEBUG": True,
    "SECRET_KEY": ":123.,2,s,"
})

# csrf.init_app(app)

CSRFProtect(app)



@app.route("/")
def index():
    title = "网站首页"
    return render_template("index.html", **locals())


@app.route("/login", methods=["GET","POST"])
def login():
    title = "登录页面"
    print(">>>> 1")
    if request.method == "GET":
        print(">>>> 2")
        token = generate_csrf()
        return render_template("login.html", **locals())
    else:
        print(">>>> 3")
        username = request.form.get("username")
        password = request.form.get("password")
        if username == "xiaoming" and password == "123456":
            print(">>>> 4")
            session["username"] = "xiaoming"
            session["is_login"] = True
            print(">>>> 6")
            return "登录成功!<br><a href='/'>返回首页</a>"
        else:
            print(">>>> 5")
            return "登录失败!<br><a href='/login'>重新登录!</a>"

if __name__ == '__main__':
    app.run()

5.3 前端模板配置

  1. 在表单中使用 CSRF 令牌:
    <form action="/login" method="post">
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
    </form>

login.html,模板代码:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
    <form action="http://127.0.0.1:5000/login" method="post">
        <input type="hidden" name="csrf_token" value="{{ token2() }}" />
        <input type="hidden" name="csrf_token" value="{{ token }}" />
        登录账号: <input type="text" name="username"> <br><br>
        登录密码: <input type="password" name="password"> <br><br>
        <button>登录</button>
    </form>
</body>
</html>

index.html,代码:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
    {% if session.get("is_login") %}
        <a href="/user">欢迎回到网站,{{ session["username"] }}</a>
    {% else %}
    <a href="{{ url_for('login') }}">登录</a>
    {% endif %}
</body>
</html>