Python Flask学习笔记之Web表单

跨站请求伪造保护

  • Flask-WTF

Flask-WTF能保护所有表单免受跨站请求伪造的攻击。为了实现CSRF防护,Flask-WTF需要为程序配置一个密钥。Flask-WTF使用这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪。

  • 设置密钥

使用类来存储配置变量,项目结构清晰,同时密钥保存在环境变量中,也增强了安全性。

touch app/config.py
# config.py
import os

class Config(object):   
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'marksecret'

配置好密钥,需要在Flask读取并使用。可以在生成Flask应用之后,利用app.config.from_object()方法来完成这个操作。

# __init__.py
from flask import Flask
from config import Config

app = Flask(__name__)
app.config.from_object(Config)

from app import routes
  • 验证密钥
>>> from app import *
>>> app.config['SECRET_KEY']
'marksecret'

表单类

touch app/forms.py
# forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired

class LoginForm(FlaskForm):
    username = StringField('username', validators=[DataRequired()])
    password = PasswordField('password', validators=[DataRequired()])
    remember_me = BooleanField('Remember Me')
    submit = SubmitField('Sign In')

把表单渲染成HTML

<!--login.html-->
{% extends "base.html" %}

{% block content %}
    <h1>Sign In</h1>
    <form action="" method="post" novalidate>
        {{ form.hidden_tag() }}
        <p>
            {{ form.username.label }}<br>
            {{ form.username(size=32) }}<br>
            {% for error in form.username.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>
            {{ form.password.label }}<br>
            {{ form.password(size=32) }}
            {% for error in form.password.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>{{ form.remember_me()}} {{ form.remember_me.label }}</p>
        <p>{{ form.submit() }}</p>
        
    </form> 
{% endblock %}       }

在视图函数中处理表单

# routes.py
from app import app
from flask import render_template,flash,redirect,url_for
from app.forms import LoginForm

@app.route('/')
@app.route('/user/<name>')
def user(name):
    return '<h1>Hello, %s</h1>' % name

@app.route('/index')
def index():
    user = {'username':'mark'}
    posts = [
        {
            'author':{'username':'mark1'},
            'body':'test1'
        },
        {
            'author':{'username':'mark2'},
            'body':'test2'
        }
    ]
    return render_template('index.html',title='home',user=user,posts=posts)

@app.route('/login',methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        flash('login requested for user {},remember_me={}'.format(form.username.data, form.remember_me.data))
        return redirect(url_for('index'))
    return render_template('login.html',title='sign in',form=form)

重定向

# 超链接
<a href="/index">Home</a>
<a href="/login">login</a>

# redirect()
return redirect('/index')

# url_for()
<a href="{{ url_for('index')}}">Home</a>
<a href="{{ url_for('login')}}">login</a>

# redirect()和url_for()
return redirect(url_for('index'))

Flash消息

请求完成后,用户需要知道状态发生了变化。这里可以使用确认消息、警告或者错误提醒。flash()函数是向用户显示消息的有效途径。模板需要将消息渲染到基础模板中,才能让所有派生出来的模板都能显示出来。

flash('login requested for user {},remember_me={}'.format(form.username.data, form.remember_me.data))
# base.html
<!DOCTYPE html>
<html>
<head>
    {% if title %}
    <title>{{ title }} - mark</title>
    {% else %}
    <title>welcome to mark's blog</title>
    {% endif %}
</head>
<body>
    <div>mark's blog 
        <a href="{{ url_for('index')}}">Home</a>
        <a href="{{ url_for('login')}}">login</a>

    </div>
    <hr>
    {% with messages = get_flashed_messages() %}
    {% if messages %}
    <ul>
        {% for message in messages %}
        <li>{{ message }}</li>
        {% endfor %}
    </ul>
    {% endif %}
    {% endwith %}
    {% block content %}{% endblock %}
</body>
</html>
posted @ 2019-07-31 16:45  mark-zh  阅读(332)  评论(0编辑  收藏  举报