现在位置: 首页 > Flask 教程 > 正文

Flask Session 与 Cookie

HTTP 协议本身是无状态的——服务器不会自动记住两次请求是否来自同一用户。

Flask 通过 Session 机制解决了这个问题,让 Web 应用能够「记住」用户的状态。


Session 的工作原理

Flask 默认使用 SecureCookieSession,它的工作方式是:

  • Session 数据被序列化后用密钥签名,保存在浏览器的 Cookie 中
  • 每次请求时,浏览器自动带上这个 Cookie
  • Flask 验证签名后,将数据还原为 Python 字典供你使用

用户可以看到 Cookie 中的内容(因为是 base64 编码的),但无法篡改——因为任何修改都会导致签名验证失败。

Session 签名 Cookie 工作机制


配置 SECRET_KEY

要使用 Session,必须先设置 SECRET_KEY——用于签名的密钥。

实例

import secrets
from flask import Flask

app = Flask(__name__)

# 生成一个安全的随机密钥(只在首次运行时生成即可)
# secrets.token_hex() 产生 64 字符的十六进制随机字符串
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

读写 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():
    # 检查 session 中是否有 username
    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
            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 中的所有数据
    session.clear()
    # 或者只删除特定 key:
    # session.pop("username", None)
    return redirect(url_for("index"))

永久 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,浏览器关闭后仍有效
        session.permanent = True
        return redirect(url_for("index"))

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"
            flash("标题不能为空", "error")
            return redirect(url_for("create_post"))

        # 保存成功消息到 flash,分类为 "success"
        flash(f"文章「{title}」发布成功!", "success")
        return redirect(url_for("create_post"))

    return render_template("create_post.html")

@app.route("/flash-demo")
def flash_demo():
    # 获取所有已 flash 的消息(读取后自动清除)
    messages = get_flashed_messages(with_categories=True)
    return render_template("flash_demo.html", messages=messages)

模板中渲染 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>
分类 推荐使用场景
"message"(默认) 一般通知
"success" 操作成功
"error" 操作失败
"warning" 警告信息
"info" 普通信息

Flash 消息依赖于 Session,因此必须设置 SECRET_KEY 才能使用。


Session 常见问题

Cookie 大小限制:浏览器对单个 Cookie 的大小限制约为 4KB。不要在 Session 中存储大量数据——适合存用户 ID、偏好设置等轻量信息,不适合存文件或大型对象。