别被骗了:Flask 的默认 Session,其实是个“伪装”的 JWT
在做全栈开发时,我们在不同的后端框架之间切换是常态。当你刚从 Spring Boot 或 Django 切到 Flask 时,很容易顺手写下 session['user_id'] = 88,并理所当然地认为:这段数据被稳妥地存到了服务器内存或数据库里。
然而,真相是:Flask 默认的 Session 根本不在服务端,它在架构思想上几乎就是一个简化版的 JWT(JSON Web Token)。
今天我们就来扒一扒 Flask Session 的底层逻辑,以及为什么原生 Flask 会让你面临“无法踢人下线”的尴尬境地。
1. 瞒天过海的 Flask Session
在原生的 Flask 应用中,当你给 session 赋值时,服务器没有开辟任何存储空间去记录它。
它的真实运作流程如下:
-
序列化与组装: Flask 将你存入的字典数据(如
{'user_id': 88})进行特定的序列化处理。 -
加密盖章: 使用你在配置文件里设定的
app.secret_key作为密钥,借助底层的itsdangerous库给这段数据打上一个数字签名。 -
发往前端: Flask 把数据内容和签名拼接在一起,变成一串长长的字符串,直接塞进 HTTP 响应的 Cookie 中发给客户端。
当客户端再次发起跨路由请求时,Flask 只是去读取 Cookie,用同样的密钥重新计算一次签名。如果对得上,就说明数据没被篡改,然后直接从里面提取出业务数据。
2. 为什么说它是“伪装的 JWT”?
如果你熟悉现代的前后端分离架构,会发现这套流程和 JWT 如出一辙:
-
彻底的无状态(Stateless): 服务端不保存任何在线用户的状态记录,只负责“发证”和“验签”。
-
防篡改,但不防偷看: 就像 JWT 只是利用 Base64 编码 Payload 一样,Flask 的 Session 默认只签名,不加密。如果你的 Cookie 被截持,或者用户自己打开浏览器控制台(F12)进行解码,里面的明文数据(比如内部的权限等级、业务字段)将会一览无余。
两者的区别仅仅在于:JWT 是跨语言、跨平台的国际通用标准,通常放置在 HTTP 请求头的 Authorization 中;而 Flask Session 是 Python 生态特定的序列化格式,死死绑定在浏览器的 Cookie 上。
3. 致命痛点:你该怎么踢人下线?
既然是无状态设计,那就必然面临无状态架构的经典难题:失去对会话的绝对控制权。
假设你的系统发现某个账号存在高危违规行为,或者检测到密码被盗,你需要立刻强行把该用户踢下线。在原生 Flask 中,你会发现自己无从下手:
-
服务端根本没有存储任何用户的会话列表,所以你无法执行类似于“删除数据库中某条 Session”的操作。
-
只要该违规用户浏览器里的那串 Cookie 还没到期,他每次发来的请求,Flask 进行数学验签后都会判定为“合法”并一路放行。
-
你唯一的物理阻断方式是修改后端的
app.secret_key——但这颗“核弹”一旦按下,全站所有在线用户的 Cookie 都会因为签名失效而瞬间报废,所有人都会被强制登出。
4. 破局之道:回归真正的服务端状态
如果你的业务场景需要严格管控在线用户、具备随时踢人下线的能力,或者需要存储大量不适合在网络中频繁传输的会话数据,千万不要直接使用 Flask 原生的 Session。
标准的工程实践是引入官方推荐的扩展库:Flask-Session。
通过引入这个库并进行极简的配置,将其与 Redis 结合:
from flask import Flask, session
from flask_session import Session
import redis
app = Flask(__name__)
# 配置 Session 存储引擎为 Redis
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.from_url('redis://127.0.0.1:6379')
Session(app)
此时,当你在业务代码里再次写下 session['user_id'] = 88 时,底层的运转机制就彻底改变了:
-
真实的业务数据
{'user_id': 88}被老老实实地存进了后端的 Redis 内存中。 -
Flask 发给前端 Cookie 的,退化成了一个极其简短且毫无意义的
Session ID(仅仅作为查询 Redis 的钥匙)。
只需几行配置,你就拿回了作为后端开发者的绝对控制权。下次再遇到需要封禁的账号,直接去 Redis 里清掉对应的 Key,违规用户的下一次请求就会被瞬间拦截。
更多推荐

所有评论(0)