1. Session的工作原理
Flask默认使用SecureCookieSession,它的工作方式是:
Session数据被序列化后用密钥签名,保存在浏览器的Cookie中
每次请求时,浏览器自动带上这个Cookie
Flask验证签名后,将数据还原为Python字典供你使用
用户可以看到Cookie中的内容(因为是base64编码的),但无法篡改——因为任何修改都会导致签名验证失败。
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐ │ 服务器 │ │ 浏览器 │ │ 服务器 │ │ │ 响应中 │ │ 后续请求 │ │ │ 设置session │ ────────> │ 存储session │ ────────> │ 验证签名 │ │ {"user":"A"}│ Set-Cookie│ 在Cookie中 │ Cookie │ 还原数据 │ │ │ │ │ │ {"user":"A"}│ └─────────────┘ └─────────────────┘ └─────────────┘2. 配置SECRET_KEY
要使用Session,必须先设置SECRET_KEY——用于签名的密钥。
import secrets from flask import Flask app = Flask(__name__) # 生成一个安全的随机密钥(只在首次运行时生成即可) app.secret_key = secrets.token_hex() # 或者直接设置一个固定值(方便开发,但不要用在生产环境) # app.secret_key = "dev-secret-key-change-in-production"
⚠️重要:
SECRET_KEY必须保密且足够随机。如果密钥泄露,任何人都可以伪造你应用的Session数据。生产环境应从环境变量或配置文件读取,不要硬编码在代码中。
生成高质量密钥的命令
$ python -c 'import secrets; print(secrets.token_hex())' 192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf
3. 读写Session
session是一个类似字典的对象,用法和Python字典基本一样:
from flask import Flask, session, redirect, url_for, request app = Flask(__name__) app.secret_key = "dev-secret-key" @app.route("/") def index(): if "username" in session: return f""" <h1>欢迎回来,{session["username"]}!</h1> <p>你的访问次数:{session.get("visits", 0)}</p> <p><a href="/logout">退出登录</a></p> """ return """ <h1>RUNOOB 首页</h1> <p>你还未登录。</p> <p><a href="/login">去登录</a></p> """ @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": username = request.form.get("username", "") if username: session["username"] = username session["visits"] = 0 return redirect(url_for("index")) return """ <form method="post"> <h2>登录 RUNOOB</h2> <p><input type="text" name="username" placeholder="输入用户名"></p> <p><input type="submit" value="登录"></p> </form> """ @app.route("/logout") def logout(): session.clear() # 清除session中的所有数据 # 或者只删除特定key: # session.pop("username", None) return redirect(url_for("index"))Session常用操作
| 操作 | 代码 | 说明 |
|---|---|---|
| 设置值 | session["key"] = "value" | 存储数据 |
| 读取值 | session.get("key", "默认值") | 安全读取,不存在时返回默认值 |
| 读取值(直接) | session["key"] | 不存在时抛出KeyError |
| 检查是否存在 | "key" in session | 返回True或False |
| 删除特定键 | session.pop("key", None) | 删除并返回值 |
| 清除所有 | session.clear() | 清空整个session |
4. 永久Session
默认情况下,Session在浏览器关闭后失效(浏览器会话级别)。如果需要「记住我」功能,可以将Session标记为永久(permanent):
from datetime import timedelta # 设置永久session的有效期(默认31天) app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(days=7) @app.post("/login-remember") def login_remember(): username = request.form.get("username") if username: session["username"] = username session.permanent = True # 标记为永久session return redirect(url_for("index"))| 配置 | 默认值 | 说明 |
|---|---|---|
PERMANENT_SESSION_LIFETIME | timedelta(days=31) | 永久session的有效期 |
5. Message Flashing——一次性消息
用户提交表单后,需要显示「操作成功」或「出错」的反馈信息。这种「显示一次后消失」的消息,Flask提供了flash()机制。
后端代码
from flask import Flask, flash, get_flashed_messages, redirect, render_template, request, url_for app = Flask(__name__) app.secret_key = "dev-secret-key" @app.route("/post", methods=["GET", "POST"]) def create_post(): if request.method == "POST": title = request.form.get("title", "").strip() if not title: flash("标题不能为空", "error") return redirect(url_for("create_post")) flash(f"文章「{title}」发布成功!", "success") return redirect(url_for("create_post")) return render_template("create_post.html")模板中渲染flash消息
templates/create_post.html:
<!DOCTYPE html> <html> <head> <title>发布文章 - RUNOOB</title> <style> .flash-success { color: green; background: #e8f5e9; padding: 10px; border-radius: 4px; } .flash-error { color: red; background: #ffebee; padding: 10px; border-radius: 4px; } .flash-info { color: blue; background: #e3f2fd; padding: 10px; border-radius: 4px; } </style> </head> <body> <h1>发布文章</h1> <!-- 获取并显示所有flash消息 --> {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% for category, message in messages %} <div class="flash-{{ category }}">{{ message }}</div> {% endfor %} {% endif %} {% endwith %} <form method="post"> <p>标题:<input type="text" name="title" style="width:300px;"></p> <p><input type="submit" value="发布"></p> </form> </body> </html>Flash消息分类
| 分类 | 推荐使用场景 |
|---|---|
"message"(默认) | 一般通知 |
"success" | 操作成功 |
"error" | 操作失败 |
"warning" | 警告信息 |
"info" | 普通信息 |
Flash消息依赖于Session,因此必须设置
SECRET_KEY才能使用。
6. Session与Cookie的对比
| 对比维度 | Session | Cookie |
|---|---|---|
| 存储位置 | 服务器端(或签名后存储在客户端) | 客户端浏览器 |
| 数据大小 | 受4KB限制(Flask默认方案) | 受4KB限制 |
| 安全性 | 较高(数据签名防篡改) | 较低(用户可见可修改) |
| 适用数据 | 用户ID、登录状态、偏好设置 | 跟踪标识、不敏感数据 |
| Flask实现 | session对象 | request.cookies读取,set_cookie()写入 |
Cookie直接操作(不通过Session)
如果需要直接读写Cookie(不签名、不加密),可以使用以下方式:
from flask import request, make_response # 读取Cookie @app.route("/get-cookie") def get_cookie(): theme = request.cookies.get("theme", "light") return f"当前主题:{theme}" # 设置Cookie @app.route("/set-cookie") def set_cookie(): resp = make_response("Cookie已设置") resp.set_cookie("theme", "dark", max_age=60*60*24) # 有效期24小时 return resp # 删除Cookie @app.route("/delete-cookie") def delete_cookie(): resp = make_response("Cookie已删除") resp.delete_cookie("theme") return resp7. Session常见问题与注意事项
Cookie大小限制
浏览器对单个Cookie的大小限制约为4KB。不要在Session中存储大量数据——适合存用户ID、偏好设置等轻量信息,不适合存文件或大型对象。
Session数据可见性
Flask默认的Session数据只是被签名(防篡改),并未加密。用户可以通过base64解码看到Session的内容。因此:
✅ 适合存储:用户ID、用户名、偏好设置(非敏感)
❌ 不适合存储:密码、信用卡号、个人隐私信息
生产环境密钥管理
import os # 从环境变量读取密钥(生产环境推荐) app.secret_key = os.environ.get("SECRET_KEY") # 如果环境变量未设置,给出明确错误提示 if not app.secret_key: raise ValueError("SECRET_KEY environment variable not set")小结
本章全面讲解了Flask的Session与Cookie机制。Session通过签名Cookie实现有状态的Web应用,使用前必须配置SECRET_KEY;session对象类似字典,支持读写、删除和清空操作;通过设置session.permanent = True可让Session在浏览器关闭后仍然有效,有效期由PERMANENT_SESSION_LIFETIME控制;flash()消息闪现机制用于一次性用户反馈,配合模板中的get_flashed_messages()使用。Session数据有4KB大小限制且仅签名未加密,适合存储用户ID等轻量非敏感信息,不适合存储敏感数据。