Flask Session 与 Cookie 新手实战指南
从零开始,手把手带你掌握 Flask 中 Session 和 Cookie 的核心用法
在 Web 开发中,HTTP 协议是"无状态"的------服务器不会记住你是谁。Cookie 和 Session 就是解决这个问题的核心技术。本文将从零开始,带你一步步掌握 Flask 中的 Session 与 Cookie 操作。
WEB项目地址:演示地址
① 核心概念解析与生活化类比
什么是 Cookie?
Cookie 是服务器发送到用户浏览器并保存在本地的一小段数据(通常不超过 4KB),浏览器在后续同源请求中会自动携带这些数据。
生活化类比 :Cookie 就像超市发给你的会员卡。第一次去超市时,收银员给你一张卡(服务器设置 Cookie),卡上记录了你的会员编号。以后每次你去这家超市,只要出示这张卡(浏览器自动携带 Cookie),收银员就知道你是谁了。
Cookie 的核心特点:
- 存储位置:客户端(浏览器)
- 传输方式 :通过 HTTP 头部传递(
Set-Cookie响应头 /Cookie请求头) - 生命周期:可设置过期时间,分为会话级(关闭浏览器失效)和持久化(到期才失效)
- 大小限制:一般不超过 4KB
什么是 Session?
Session 是服务器端存储的用户会话数据。用户访问网站时,服务器会创建一个 Session,并通过 Cookie 把 Session ID 发给浏览器。
生活化类比 :Session 就像超市的会员档案柜。你的会员卡(Cookie)上只写了一个卡号(Session ID),而超市的档案柜里(服务器端)存着你的完整信息------姓名、积分、购买记录等。
Cookie vs Session 对比
| 对比维度 | Cookie | Session |
|---|---|---|
| 存储位置 | 客户端(浏览器) | 服务端 |
| 存储大小 | 约 4KB 限制 | 理论上无限制 |
| 安全性 | 较低(数据可被用户查看) | 较高(数据在服务端) |
| 生命周期 | 可自由设置过期时间 | 由服务端控制 |
Flask 中的 Session 机制
Flask 默认将 Session 数据以签名 Cookie 的形式存储在客户端。数据经过加密签名,用户可以看到内容但无法篡改(除非知道密钥)。
关键点 :Flask 默认的 Session 不是加密的,而是签名的 。签名防止篡改,但不防止查看。所以永远不要在 Session 中存储密码、银行卡号等敏感信息。
② 开发环境搭建与依赖安装
环境准备
确保你已经安装了 Python 3.8 或以上版本。
创建虚拟环境(推荐)
bash
# 创建项目目录
mkdir flask-session-demo
cd flask-session-demo
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境(Windows)
venv\Scripts\activate
# 激活虚拟环境(Mac/Linux)
source venv/bin/activate
安装 Flask
bash
pip install Flask
如果需要使用 Redis 存储 Session(生产环境推荐),可以安装 Flask-Session:
bash
pip install Flask-Session[redis]
验证安装
创建 app.py 文件,写入以下代码测试环境是否正常:
python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, Flask!'
if __name__ == '__main__':
app.run(debug=True)
运行 python app.py,浏览器访问 http://127.0.0.1:5000,看到 "Hello, Flask!" 即表示环境搭建成功。
③ Cookie 的基础设置与读取操作
设置 Secret Key(必须!)
在使用 Session 之前,必须先设置 SECRET_KEY。这是 Flask 签名 Session Cookie 的密钥:
python
from flask import Flask
app = Flask(__name__)
app.secret_key = 'your-secret-key-here' # 开发环境用固定值
# 生产环境请用随机字符串,不要硬编码!
设置 Cookie
Cookie 通过响应对象(make_response)的 set_cookie() 方法设置:
python
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/set-cookie')
def set_cookie():
resp = make_response("Cookie 已设置")
# 基本设置
resp.set_cookie('username', 'alice')
# 带过期时间(24小时,单位:秒)
resp.set_cookie('theme', 'dark', max_age=86400)
# 设置路径和域名限制
resp.set_cookie('preference', 'zh-CN', path='/', domain=None)
return resp
set_cookie() 常用参数:
| 参数 | 说明 | 示例 |
|---|---|---|
key |
Cookie 名称 | 'username' |
value |
Cookie 值(字符串) | 'alice' |
max_age |
最大存活时间(秒) | 3600(1小时) |
expires |
过期时间(datetime 对象) | datetime(2025, 12, 31) |
path |
Cookie 生效路径 | '/' |
domain |
Cookie 生效域名 | '.example.com' |
httponly |
禁止 JavaScript 访问 | True |
secure |
仅通过 HTTPS 发送 | True |
设置多个 Cookie
python
@app.route('/set-multiple-cookies')
def set_multiple():
resp = make_response("多个 Cookie 已设置")
resp.set_cookie('username', 'alice')
resp.set_cookie('user_id', '10086')
resp.set_cookie('preference', 'dark_mode')
return resp
设置带过期时间的 Cookie
方法一:使用 max_age(秒数)
python
@app.route('/set-cookie-10s')
def set_cookie_10s():
resp = make_response("Cookie 将在10秒后过期")
resp.set_cookie('temp', 'short_lived', max_age=10)
return resp
方法二:使用 expires(datetime 对象)
python
import datetime
@app.route('/set-cookie-1day')
def set_cookie_1day():
resp = make_response("Cookie 将在1天后过期")
expires_time = datetime.datetime.now() + datetime.timedelta(days=1)
resp.set_cookie('persistent', 'long_lived', expires=expires_time)
return resp
读取 Cookie
通过 request.cookies 获取所有 Cookie,它是一个类似字典的对象:
python
from flask import request
@app.route('/get-cookie')
def get_cookie():
# 获取所有 Cookie
all_cookies = request.cookies
print(all_cookies)
# 获取单个 Cookie(不存在时返回 None)
username = request.cookies.get('username')
return f'当前用户: {username}'
删除 Cookie
删除 Cookie 实际上是将它设置为过期:
python
@app.route('/delete-cookie')
def delete_cookie():
resp = make_response("Cookie 已删除")
resp.set_cookie('username', '', max_age=0)
# 或者使用 expires 设置为过去时间
# resp.set_cookie('username', '', expires=0)
return resp
④ Session 机制开启与数据存储
启用 Session
使用 Session 需要:
- 设置
SECRET_KEY - 从
flask导入session
python
from flask import Flask, session
app = Flask(__name__)
app.secret_key = 'your-secret-key-here' # 必须设置!
Session 的基本操作
Flask 的 session 对象使用起来就像字典一样:
python
from flask import Flask, session, request, redirect, url_for
app = Flask(__name__)
app.secret_key = 'your-secret-key-here'
# 写入 Session
@app.route('/set-session')
def set_session():
session['username'] = 'alice'
session['user_id'] = 10086
session['roles'] = ['admin', 'editor'] # 可以存储列表
return 'Session 数据已存储'
# 读取 Session
@app.route('/get-session')
def get_session():
username = session.get('username', '未登录')
user_id = session.get('user_id')
roles = session.get('roles', [])
return f'用户: {username}, ID: {user_id}, 角色: {roles}'
# 删除 Session 中的某个键
@app.route('/delete-session-key')
def delete_session_key():
session.pop('username', None) # 安全删除,不存在也不报错
return 'username 已删除'
# 清空整个 Session
@app.route('/clear-session')
def clear_session():
session.clear()
return 'Session 已清空'
# 检查 Session 中是否存在某个键
@app.route('/check-session')
def check_session():
if 'username' in session:
return f'用户已登录: {session["username"]}'
return '用户未登录'
Session 的默认行为
Flask 默认将 Session 数据存储在客户端的 Cookie 中(签名后)。这意味着:
- Session 数据大小受 Cookie 4KB 限制
- 数据可以被用户查看(Base64 解码),但无法篡改
- 不适合存储大量或敏感数据
⑤ 用户登录状态保持完整流程
完整的登录/登出示例
下面实现一个完整的用户登录状态管理:
python
from flask import Flask, session, request, redirect, url_for, render_template_string
app = Flask(__name__)
app.secret_key = 'your-secret-key-here'
# 模拟用户数据库
users = {
'admin': '123456',
'test': 'password'
}
# 登录页面(使用简单的 HTML 模板)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
# 验证用户名和密码
if username in users and users[username] == password:
# 登录成功:存储用户信息到 Session
session['username'] = username
session['logged_in'] = True
# 判断是否勾选"记住我"
remember = request.form.get('remember')
if remember:
# 设置为永久会话
session.permanent = True
return redirect(url_for('dashboard'))
else:
return '用户名或密码错误,请重试'
# GET 请求返回登录表单
return '''
<form method="post">
<p>用户名: <input type="text" name="username"></p>
<p>密码: <input type="password" name="password"></p>
<p><input type="checkbox" name="remember"> 记住我</p>
<p><input type="submit" value="登录"></p>
</form>
'''
# 仪表盘(需要登录才能访问)
@app.route('/dashboard')
def dashboard():
if 'username' not in session:
return redirect(url_for('login'))
return f'欢迎回来,{session["username"]}! <a href="/logout">退出登录</a>'
# 退出登录
@app.route('/logout')
def logout():
session.pop('username', None)
session.pop('logged_in', None)
return '已退出登录,<a href="/login">重新登录</a>'
if __name__ == '__main__':
app.run(debug=True)
"记住我"功能的实现
上面的代码已经包含了"记住我"的核心逻辑:
python
if remember:
session.permanent = True # 设置为永久会话
配合 PERMANENT_SESSION_LIFETIME 配置,可以控制"记住我"的有效时长:
python
from datetime import timedelta
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # 记住我 7 天
工作流程图解
用户 → 提交登录表单 → 服务器验证凭据
↓
验证通过
↓
服务器创建 Session
生成 session_id
↓
响应头 Set-Cookie: session_id=xxx
↓
用户 ← 浏览器保存 Cookie ← 服务器返回登录成功
↓
用户访问 /dashboard
浏览器自动携带 Cookie
↓
服务器根据 session_id 查找用户状态
↓
用户 ← 返回仪表盘页面
⑥ 会话过期时间与安全性配置
设置 Session 过期时间
方法一:全局配置永久会话过期时间
python
from datetime import timedelta
# 设置为 30 分钟过期
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=30)
方法二:在视图函数中动态设置
python
@app.route('/login', methods=['POST'])
def login():
# ... 验证逻辑 ...
session['username'] = username
session.permanent = True # 标记为永久会话
# 过期时间由 PERMANENT_SESSION_LIFETIME 控制
return redirect(url_for('dashboard'))
方法三:会话级 Session(浏览器关闭即失效)
python
# 默认情况下,不设置 permanent 的 Session 在浏览器关闭时失效
# 也可以显式配置:
app.config['SESSION_PERMANENT'] = False
Session 刷新机制
每次用户访问时重置过期时间:
python
@app.before_request
def refresh_session():
# 每次请求前刷新 Session,延长过期时间
session.modified = True
安全配置详解
Flask 默认只开启了 HttpOnly,其他安全配置需要手动设置:
python
# ====== 安全配置(生产环境必看)======
# 1. SECRET_KEY:签名密钥(必须!)
app.secret_key = 'your-strong-secret-key' # 生产环境用随机字符串
# 2. HttpOnly:禁止 JavaScript 读取 Cookie(默认已开启)
app.config['SESSION_COOKIE_HTTPONLY'] = True # 默认就是 True
# 3. Secure:仅通过 HTTPS 传输 Cookie(生产环境必须开启!)
app.config['SESSION_COOKIE_SECURE'] = True # 默认 False
# 4. SameSite:防止 CSRF 攻击
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # 推荐 'Lax'
# 5. 会话过期时间
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2)
# 6. Session Cookie 名称(可选,默认 'session')
app.config['SESSION_COOKIE_NAME'] = 'myapp_session' #
各安全配置说明
| 配置项 | 默认值 | 生产环境建议 | 作用 |
|---|---|---|---|
SESSION_COOKIE_HTTPONLY |
True |
True |
防止 XSS 窃取 Cookie |
SESSION_COOKIE_SECURE |
False |
True |
仅 HTTPS 传输 |
SESSION_COOKIE_SAMESITE |
未设置 | 'Lax' |
防止 CSRF 攻击 |
PERMANENT_SESSION_LIFETIME |
31天 | 根据业务调整 | 控制会话有效期 |
完整的安全配置示例
python
from datetime import timedelta
app.config.update(
SECRET_KEY='your-production-secret-key',
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_SAMESITE='Lax',
PERMANENT_SESSION_LIFETIME=timedelta(hours=2),
SESSION_COOKIE_NAME='myapp_session'
)
⑦ 常见报错排查与调试技巧
问题 1:Session 数据不生效 / 无法跨请求保持
排查步骤:
-
检查 Secret Key 是否设置
python# 确认已设置 print(app.secret_key) # 不应为 None -
检查响应头是否包含 Set-Cookie
- 打开浏览器开发者工具 → Network → 查看响应头
- 确认是否有
Set-Cookie头
-
检查请求头是否携带 Cookie
- 浏览器开发者工具 → Network → 查看请求头
- 确认是否有
Cookie头
-
打印调试信息
python@app.route('/debug-session') def debug_session(): print('请求中的 Cookie:', request.cookies) print('当前 Session:', dict(session)) return '请查看控制台输出'
问题 2:Session 数据在生产环境丢失
可能原因:
- 多进程/多服务器部署时,Session 默认存储在内存中,不同进程无法共享
- 使用了
SESSION_TYPE='filesystem'但目录权限不足
解决方案:
- 使用 Redis 等共享存储(见第⑧节)
问题 3:Cookie 大小超限
Flask 默认将 Session 存储在 Cookie 中,大小不能超过 4KB。
症状:Session 数据静默丢失,浏览器可能拒绝写入 Cookie。
解决方案:
- 减少 Session 中存储的数据量
- 使用 Flask-Session 将 Session 存储在服务端(Redis/数据库)
问题 4:SECRET_KEY 不一致导致 Session 失效
症状:重启应用后所有用户 Session 失效。
原因 :Flask 使用 SECRET_KEY 签名 Session Cookie。如果每次启动都生成新的随机密钥,旧的 Session 签名无法验证。
解决方案:
- 使用固定的
SECRET_KEY(从环境变量读取) - 不要使用
os.urandom()动态生成
问题 5:Safari 浏览器 Session 失效
Safari 默认阻止第三方 Cookie。
解决方案:
- 确保应用和 API 在同一域名下(第一方 Cookie)
- 或配置
SESSION_COOKIE_SAMESITE='None'并配合SECURE=True
调试技巧汇总
- 使用浏览器开发者工具:查看 Cookie 的写入和读取
- 打印关键变量 :
print(request.cookies)和print(dict(session)) - 检查 Flask 日志 :启动时添加
debug=True查看详细日志 - 使用 Flask 内置的 Session 调试:检查 Session 是否被标记为 modified
⑧ 生产环境部署注意事项
1. SECRET_KEY 管理
❌ 错误做法:
python
app.secret_key = 'hardcoded_secret' # 硬编码在代码中
app.secret_key = 'dev' # 使用弱密钥
✅ 正确做法:
python
import os
# 从环境变量读取
app.secret_key = os.environ.get('SECRET_KEY')
# 或使用 Python 生成随机密钥(一次性)
# python -c "import secrets; print(secrets.token_hex(32))"
生产环境密钥要求:
- 使用高强度随机字符串
- 通过环境变量或密钥管理服务注入
- 开发/测试/生产环境使用不同密钥
- 定期轮换密钥(需考虑旧 Session 失效问题)
2. 安全 Cookie 配置
python
# 生产环境配置
app.config.update(
SESSION_COOKIE_SECURE=True, # 仅 HTTPS
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
PERMANENT_SESSION_LIFETIME=timedelta(hours=2),
# SESSION_COOKIE_DOMAIN 不要硬编码,保持为 None
)
注意 :SESSION_COOKIE_DOMAIN 不要硬编码域名,保持为 None。
3. 使用 Flask-Session 实现服务端存储
默认的 Cookie 存储有 4KB 限制且不安全。生产环境建议使用 Redis 等存储 Session:
python
from flask import Flask
from flask_session import Session
import redis
app = Flask(__name__)
# 配置 Redis 存储
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.Redis(host='localhost', port=6379, db=0)
app.config['SESSION_PERMANENT'] = False # 或 True
app.config['SESSION_USE_SIGNER'] = True
app.config['SESSION_KEY_PREFIX'] = 'myapp:'
# 初始化 Flask-Session
Session(app)
安装依赖:
bash
pip install Flask-Session[redis]
4. 多服务器/负载均衡场景
如果应用部署在多台服务器上,必须使用共享存储(如 Redis):
- 所有服务器实例连接同一个 Redis
- 确保
SECRET_KEY在所有实例上一致
5. 关闭调试模式
python
# 生产环境
app.debug = False
app.config['DEBUG'] = False
6. 使用环境感知配置
python
import os
if os.environ.get('FLASK_ENV') == 'production':
app.config['SESSION_COOKIE_SECURE'] = True
app.config['DEBUG'] = False
else:
app.config['SESSION_COOKIE_SECURE'] = False
app.config['DEBUG'] = True
7. Cookie 大小监控
如果使用默认的 Cookie 存储,注意监控 Session 数据大小。当序列化的 Cookie 值超过约 4093 字节时,Werkzeug 会发出警告。
生产环境部署检查清单
-
SECRET_KEY通过环境变量注入,未硬编码 -
SESSION_COOKIE_SECURE = True(使用 HTTPS) -
SESSION_COOKIE_HTTPONLY = True -
SESSION_COOKIE_SAMESITE = 'Lax'或'Strict' - 生产环境
DEBUG = False - 多服务器场景使用 Redis 等共享存储
-
PERMANENT_SESSION_LIFETIME设置了合理的过期时间 - 不在 Session 中存储敏感信息