跨站请求伪造保护
- 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
{% extends "base.html" %}
{% block content %}
Sign In
{% 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/')
def user(name):
return 'Hello, %s
' % 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)
重定向
# 超链接
Home
login
# redirect()
return redirect('/index')
# url_for()
Home
login
# 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
{% if title %}
{{ title }} - mark
{% else %}
welcome to mark's blog
{% endif %}
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
- {{ message }}
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}