账号会话严格隔离不是“不让共享”,而是“主动划界”——从客户端标识、服务端存储到请求处理,三层切断交叉路径,才能让多任务并行不串、不扰、不泄。

客户端标识必须带身份锚点
浏览器默认按域名发 Cookie,这是混号的起点。必须让每个会话自带唯一身份标记:
- Web 应用中,调用
session_set_cookie_params()显式限定domain(如user1.app.com)和path(如/dashboard),禁止跨子域读取 - 前端 API 请求统一携带
X-User-ID或解析 JWT 中的sub字段,后端不再依赖 Cookie 自动关联会话 - Streamlit / Gradio 类工具,避免直接写
st.session_state.messages,改用命名空间键名,例如st.session_state[f"chat_{user_id}_history"]
服务端存储要物理分仓
即使前端传对了用户 ID,若数据全塞进一张表或一个 Redis key,照样会覆盖或错读:
- 数据库层面:统一用带
user_id字段的表,所有查询强制加WHERE user_id = ?;高敏感场景可为关键业务(如支付、文档)单独建表,表名含用户标识(如payment_log_2048) - Redis 缓存:键名必须哈希化,例如
user:profile:{user_id}、task:queue:{user_id}:{task_type},禁用泛型 key 如cache:user - 文件系统:PHP/Python 的 session 存储路径需按用户隔离,如
/var/tmp/sessions/user_123/,权限设为700,避免其他进程越权访问
请求处理流程要独立调度
并发下线程/协程复用容易导致上下文污染,尤其在模型推理、批量任务等长耗时场景:
- 入口处即校验身份,JWT 过期、签名无效、user_id 为空,一律拒之门外,不进入后续逻辑
- 为每个用户维护独立内存缓冲区,加载历史时只取该用户最近 N 条,并做 token 截断,防止超上下文长度
- 使用带分组能力的队列(如 Celery 的
routing_key或 Pythonqueue.Queue+ 用户 ID 哈希),确保同一用户的请求始终由同一线程/worker 处理
权限与空间需绑定执行上下文
隔离不止于数据,还要约束行为边界:
- 双用户体系(如管理员+会员)必须区分权限前缀:
system:user:add与member:order:pay不可混用,RBAC 规则按命名空间校验 - Kotaemon / RAG 类系统中,“空间(Space)”是隔离单元,用户须被显式邀请才能访问,上传文档、提问记录、知识库索引均绑定空间 ID,不可跨空间检索
- 任务调度层强制绑定
account_key(如 py12306),每个定时查询、抢票动作只归属指定账号,避免任务误执行在他人会话上

















