Flask 中的 Session 和 Cookies
在构建 web 应用时,管理用户的状态和数据是至关重要的。Flask,作为一个灵活的微型 web 框架,提供了会话(Session)和 Cookies 管理的能力。本文将深入探讨 Flask 中的会话和 Cookies 的概念、工作机制以及应用实例,为读者提供全面而详细的理解。
会话和 Cookies 的基本概念
Cookies
- 定义:Cookies 是服务器存储在用户浏览器上的小片段数据,每次浏览器向服务器发送请求时都会附带这些数据。
- 用途:主要用于记住用户信息(如登录状态)、跟踪用户访问模式等。
会话(Session)
- 定义:会话是一种在服务器上存储用户数据的方式,用于跨请求保持状态。
- 用途:常用于存储用户特定的信息,如登录后的用户ID、购物车内容等。
Cookies 的使用
在 Flask 中操作 Cookies 是非常简单直观的。
设置 Cookies
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/set_cookie')
def set_cookie():
response = make_response('Cookie has been set')
response.set_cookie('username', 'John Doe')
return response
获取 Cookies
from flask import request
@app.route('/get_cookie')
def get_cookie():
username = request.cookies.get('username')
return 'The username in cookie is: ' + str(username)
Cookies 的局限性
- 存储在用户浏览器端
,因此容易受到安全攻击,如跨站脚本(XSS)和跨站请求伪造(CSRF)。
- Cookies 的大小通常限制在 4KB 左右,不适合存储大量数据。
Flask 会话(Session)的使用
会话(Session)在 Flask 中用于存储在服务端的用户数据,而浏览器只保存一个会话ID的 Cookie。
配置 Flask 会话
在 Flask 应用中,需要设置一个密钥来加密会话数据。
app = Flask(__name__)
app.secret_key = 'your_secret_key' # 应为难以猜测的密钥
设置会话数据
from flask import session
@app.route('/login')
def login():
session['user_id'] = '123456' # 假设用户ID为123456
return 'User logged in'
获取会话数据
@app.route('/profile')
def profile():
user_id = session.get('user_id')
if not user_id:
return 'Not logged in!', 403
return 'Profile page for user {}'.format(user_id)
会话的有效期
默认情况下,Flask 的会话是浏览器关闭时过期。也可以设置会话的持续时间:
from datetime import timedelta
app.permanent_session_lifetime = timedelta(days=5)
session.permanent = True # 使当前会话持久化
会话与 Cookies 的安全性
安全性是管理会话和 Cookies 时必须考虑的重要因素。
安全实践
- 使用 HTTPS 来防止会话被窃听。
- 设置 Cookie 的
secure
标志,使其仅通过 HTTPS 发送。 - 设置 Cookie 的
HttpOnly
标志,阻止 JavaScript 访问 Cookie。 - 定期更换
secret_key
。 - 对敏感数据进行加密处理。
使用 Flask-Session 扩展
对于需要更复杂会话管理的应用,可以使用 Flask-Session 扩展,它支持将会话数据保存在服务器端的多种后端中,例如 Redis、文件系统等。
flask-session
# 1 第三方 flask-session,可以把session的内容保存在服务端
-redis
-数据库
-文件。。。
# 2 安装并使用
pip3 install flask-session
第一种
from flask_session.redis import RedisSessionInterface
import redis
app = Flask(__name__)
app.secret_key='adsfasdfads'
conn=redis.Redis(host='127.0.0.1',port=6379)
# 1 client:redis链接对象
# 2 key_prefix:放到redis中得前缀
# 3 use_signer:是否使用secret_key 加密
# 4 permanent:关闭浏览器,cookie是否失效
# 5 生成session_key的长度
app.session_interface=RedisSessionInterface(app,client=conn,key_prefix='session',use_signer=True, permanent=True, sid_length=32)
第二种(推荐)
from flask import Flask,session
from flask_session import Session
from redis import Redis
app = Flask(__name__)
app.secret_key='asdfasdf'
app.debug=True
# 配置信息,可以写在 配置文件中
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1', port='6379')
# app.config['SESSION_KEY_PREFIX'] = 'lqz' # 如果不写,默认以:SESSION_COOKIE_NAME 作为key
# app.config.from_pyfile('./settings.py')
Session(app) # 核心跟第一种方式一模一样
from flask import Flask, session
from flask_session.redis import RedisSessionInterface
import redis
app = Flask(__name__)
conn = redis.Redis(host='127.0.0.1', port=6379)
# use_signer是否对key签名
app.session_interface = RedisSessionInterface(conn, key_prefix='lqz')
@app.route('/')
def hello_world():
session['name'] = 'jing'
return 'Hello World!'
@app.route('/index')
def index():
res = session['name']
return 'Hello World!' + res
if __name__ == '__main__':
app.run()
from redis import Redis
SESSION_TYPE = 'redis'
SESSION_REDIS = Redis(host='127.0.0.1', port='6379')
wtforms
在Flask中,WTForms库主要用于处理Web表单的创建、验证和渲染。具体来说,WTForms在Flask中有以下几个关键用途:
- 表单验证:
- WTForms提供了丰富的验证器,如
DataRequired
、Length
等,用于验证用户提交的数据是否符合系统要求。 - 通过这些验证器,可以确保表单数据的完整性、准确性和安全性。
- 例如,可以使用
DataRequired
验证器来确保某个字段(如用户名或密码)必须被填写。
- WTForms提供了丰富的验证器,如
- 定义表单字段:
- WTForms允许用户定义各种类型的表单字段,如文本字段、密码字段、单选按钮、复选框等。
- 字段的定义与HTML表单元素相对应,使得开发者能够轻松地构建复杂的表单结构。
- 渲染表单为HTML:
- 虽然WTForms本身不直接渲染HTML,但它可以与Flask的模板引擎(如Jinja2)结合使用,将表单渲染为HTML。
- 这使得开发者能够灵活控制表单的显示样式和布局。
- 与Flask框架集成:
- Flask-WTF是WTForms的Flask扩展,它简化了WTForms在Flask中的使用。
- Flask-WTF提供了额外的功能,如CSRF保护、文件上传等,增强了表单的安全性。
- 自定义验证规则:
- 除了内置的验证器外,WTForms还支持自定义验证规则。
- 这使得开发者能够根据自己的业务需求来定义特定的验证逻辑。
- 表单继承:
- WTForms支持表单继承,允许开发者创建基类表单,并在子类中扩展和覆盖字段和验证规则。
- 这有助于实现代码的重用和模块化。
# pip install WTForms
# django--->forms组件
-1 校验数据
-2 错误处理
-3 渲染页面
# flask--》第三方的wtforms
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
app = Flask(__name__, template_folder='templates')
app.debug = True
class LoginForm(Form):
# 字段(内部包含正则表达式)
name = simple.StringField(
label='用户名',
validators=[
validators.DataRequired(message='用户名不能为空.'),
validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
],
widget=widgets.TextInput(), # 页面上显示的插件
render_kw={'class': 'form-control'}
)
# 字段(内部包含正则表达式)
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空.'),
validators.Length(min=8, message='用户名长度必须大于%(min)d'),
validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
form = LoginForm()
return render_template('wtforms_login.html', form=form)
else:
form = LoginForm(formdata=request.form)
if form.validate():
print('用户提交数据通过格式验证,提交的值为:', form.data)
else:
print(form.errors)
return render_template('wtforms_login.html', form=form)
if __name__ == '__main__':
app.run()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<form method="post">
<p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>
<p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>