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 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!" 即表示环境搭建成功。

设置 Secret Key(必须!)

在使用 Session 之前,必须先设置 SECRET_KEY。这是 Flask 签名 Session Cookie 的密钥:

python 复制代码
from flask import Flask

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'  # 开发环境用固定值
# 生产环境请用随机字符串,不要硬编码!

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
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

方法一:使用 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

通过 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 实际上是将它设置为过期:

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 需要:

  1. 设置 SECRET_KEY
  2. 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 数据不生效 / 无法跨请求保持

排查步骤

  1. 检查 Secret Key 是否设置

    python 复制代码
    # 确认已设置
    print(app.secret_key)  # 不应为 None
  2. 检查响应头是否包含 Set-Cookie

    • 打开浏览器开发者工具 → Network → 查看响应头
    • 确认是否有 Set-Cookie
  3. 检查请求头是否携带 Cookie

    • 浏览器开发者工具 → Network → 查看请求头
    • 确认是否有 Cookie
  4. 打印调试信息

    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

调试技巧汇总

  1. 使用浏览器开发者工具:查看 Cookie 的写入和读取
  2. 打印关键变量print(request.cookies)print(dict(session))
  3. 检查 Flask 日志 :启动时添加 debug=True 查看详细日志
  4. 使用 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 失效问题)
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

如果使用默认的 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 中存储敏感信息
相关推荐
码云骑士42 分钟前
104-实战论文搜索引擎-ArXiv爬取-Milvus存储-RAG问答-Gradio前端
前端·python·搜索引擎·milvus
比高创意品牌策划设计1 小时前
零售卖场门头设计怎么做才显眼
python
2601_953988071 小时前
Ricon组态系统vs传统组态软件:为什么选择新一代Web组态平台
前端·后端·物联网·tcp/ip·数学建模·前端框架
IT_陈寒1 小时前
SpringBoot自动配置坑了我三天,原来漏了这个注解
前端·人工智能·后端
鬼手点金1 小时前
Scrapy + Playwright 完整示例(JS 动态渲染网页)
开发语言·javascript·爬虫·python·scrapy·html·json
存在morning1 小时前
【Python 开发实践 一】Python vs Go vs Java 三门语言对比
java·python·golang
vx-程序开发1 小时前
springboot旅游推介平台---附源码24175
java·spring boot·python·spring cloud·eclipse·django·idea
心运软件2 小时前
基于深度学习的宝石图像分类系统
人工智能·python·深度学习·机器学习·分类·数据挖掘
一木 之林2 小时前
AI实战 : Numpy图像处理与深度学习框架
python