Flask入门教程(二十六):Session API——用户会话状态管理

1. session代理对象

session是一个代理,指向当前请求的SessionMixin实例。只在请求上下文中可用

属性/方法 说明
session[key] = value 设置session值
session.get(key, default) 安全获取session值,键不存在时返回default
session.pop(key, default) 移除并返回指定键的值
session.clear() 清空所有session数据
"key" in session 检查键是否存在
session.permanent 设为True时,Session在浏览器关闭后仍保持
session.modified 修改嵌套可变对象时需手动设为True
session.accessed session被访问时为True
session.new session是否为新创建

2. 基本操作

复制代码
from flask import Flask, session, redirect, url_for, request

app = Flask(__name__)
app.secret_key = "your-secret-key"

# 设置值
@app.route("/set")
def set_session():
    session["username"] = "runoob"
    session["user_id"] = 1001
    session["preferences"] = {"theme": "dark", "lang": "zh"}
    return "Session已设置"

# 读取值
@app.route("/get")
def get_session():
    username = session.get("username", "访客")  # 安全获取
    # 或直接访问(可能抛出KeyError)
    # username = session["username"]
    return f"用户名: {username}"

# 检查是否存在
@app.route("/check")
def check_session():
    if "username" in session:
        return f"用户已登录: {session['username']}"
    return "未登录"

# 删除特定键
@app.route("/remove")
def remove_session():
    session.pop("username", None)
    return "username已移除"

# 清空全部
@app.route("/clear")
def clear_session():
    session.clear()
    return "Session已清空"

3. 永久Session

默认情况下,Session在浏览器关闭后失效 (会话级别)。通过设置session.permanent = True可使其在浏览器关闭后仍然有效,有效期由PERMANENT_SESSION_LIFETIME配置控制。

复制代码
from datetime import timedelta

# 配置:设置永久session有效期为7天
app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(days=7)

@app.route("/login", methods=["POST"])
def login():
    session["username"] = request.form.get("username")
    # 标记为永久session
    session.permanent = True
    return redirect(url_for("index"))

4. 重要:modified标记

当修改session中嵌套的可变对象 (如列表、字典)时,Flask无法自动检测到修改 。必须手动设置session.modified = True,否则修改不会保存到Cookie中。

复制代码
@app.route("/append")
def append_item():
    # ❌ 错误:直接修改列表,Flask检测不到
    if "items" not in session:
        session["items"] = []
    session["items"].append("new_item")
    # 修改不会保存!

    # ✅ 正确:手动标记modified
    if "items" not in session:
        session["items"] = []
    session["items"].append("new_item")
    session.modified = True  # 必须手动标记!
    return f"Items: {session['items']}"

# 替代方案:重新赋值(自动触发modified)
@app.route("/set-list")
def set_list():
    items = session.get("items", [])
    items.append("new_item")
    session["items"] = items  # 重新赋值,Flask能检测到
    return f"Items: {session['items']}"
操作类型 是否需要手动modified=True
session["key"] = value(赋值) ❌ 否(自动检测)
session.pop("key") ❌ 否(自动检测)
session["list"].append(x)(修改嵌套对象) 是(必须手动)
session["dict"]["key"] = value(修改嵌套字典) 是(必须手动)

5. Session相关配置项

配置键 默认值 说明
SECRET_KEY None 签名Session的密钥,必须设置才能使用Session
SESSION_COOKIE_NAME "session" Session Cookie的名称
SESSION_COOKIE_DOMAIN None Cookie的Domain属性
SESSION_COOKIE_PATH None Cookie的Path属性
SESSION_COOKIE_HTTPONLY True 禁止JavaScript访问Session Cookie
SESSION_COOKIE_SECURE False True时仅通过HTTPS发送
SESSION_COOKIE_SAMESITE None "Strict""Lax"None
PERMANENT_SESSION_LIFETIME timedelta(days=31) 永久Session的有效期
SESSION_REFRESH_EACH_REQUEST True 每次请求都刷新Session Cookie的有效期

生产环境Session配置推荐

复制代码
# app.py
import os
from datetime import timedelta

app.config.update(
    SECRET_KEY=os.environ.get("SECRET_KEY"),  # 从环境变量读取
    SESSION_COOKIE_SECURE=True,                # 仅HTTPS传输
    SESSION_COOKIE_HTTPONLY=True,              # 禁止JavaScript访问
    SESSION_COOKIE_SAMESITE="Lax",             # 防范CSRF
    PERMANENT_SESSION_LIFETIME=timedelta(days=7),  # 7天有效期
    SESSION_REFRESH_EACH_REQUEST=False,        # 不自动刷新,减少Cookie写入
)

6. 完整代码示例

复制代码
from flask import Flask, session, redirect, url_for, request
from datetime import timedelta

app = Flask(__name__)
app.secret_key = "dev-secret-key"
app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(days=7)

@app.route("/")
def index():
    username = session.get("username", "访客")
    items = session.get("items", [])
    return f"""
    <h1>欢迎, {username}!</h1>
    <p>购物车: {items}</p>
    <p><a href="/login">登录</a> | <a href="/add-item">添加商品</a> | <a href="/logout">退出</a></p>
    """

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        session["username"] = request.form.get("username", "用户")
        session["items"] = []
        session.permanent = True
        return redirect(url_for("index"))
    return '<form method="post"><input name="username"><input type="submit"></form>'

@app.route("/add-item")
def add_item():
    if "items" not in session:
        session["items"] = []
    session["items"].append(f"商品{len(session['items']) + 1}")
    session.modified = True  # 修改嵌套列表,必须手动标记!
    return redirect(url_for("index"))

@app.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("index"))

7. Session API速查表

操作 代码
设置值 session["key"] = "value"
读取值(安全) session.get("key", "default")
读取值(直接) session["key"](键不存在抛KeyError)
检查键是否存在 "key" in session
删除键 session.pop("key", None)
清空所有 session.clear()
设置永久 session.permanent = True
标记已修改 session.modified = True
获取所有键 list(session.keys())

8. 安全注意事项

⚠️ 风险 ✅ 防护措施
密钥泄露导致Session伪造 SECRET_KEY从环境变量读取,不提交到Git
Session被窃取(中间人攻击) 生产环境启用SESSION_COOKIE_SECURE=True
XSS窃取Session 使用SESSION_COOKIE_HTTPONLY=True
CSRF攻击 配合SESSION_COOKIE_SAMESITE="Lax"或使用Flask-WTF
复制代码
# 生产环境密钥管理最佳实践
import os
import secrets

# 开发环境:自动生成,但提交到Git时忽略
if os.environ.get("FLASK_ENV") == "development":
    app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY", secrets.token_hex(32))
else:
    # 生产环境:必须从环境变量读取
    app.config["SECRET_KEY"] = os.environ["SECRET_KEY"]

小结

本章全面讲解了Flask Session的完整API。session代理对象支持字典式操作(session[key]session.get()session.pop()session.clear()in检查);永久Session通过session.permanent = True启用,有效期由PERMANENT_SESSION_LIFETIME控制;修改嵌套可变对象(如列表append、字典嵌套赋值)时必须手动设置session.modified = True,否则修改不会保存;相关配置项包括SECRET_KEY(必须设置)、SESSION_COOKIE_SECURESESSION_COOKIE_HTTPONLYSESSION_COOKIE_SAMESITE等。Session默认存储在签名Cookie中,数据有4KB大小限制且仅签名未加密,适合存储用户ID等轻量非敏感信息。

相关推荐
子兮曰2 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰2 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
默_笙2 天前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
爱勇宝2 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
qq_426003962 天前
启动playwright录制codegen生成自动化测试脚本
python·自动化
虎头金猫2 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
胡写代码2 天前
别再前后端各写一套表单校验了
java·后端
长沙三为智能科技2 天前
家政小程序开发从0到上线:五阶段交付流程与验收清单
python
伞伞悦读2 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
大勇前进2 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端