Redis租户隔离必须强制频道名带tenant:{tenant_id}:前缀并服务端校验,禁用PSUBSCRIBE,配合ACL按租户配置~tenant:id:*权限,缺一不可。

Redis 的 PUBLISH/SUBSCRIBE 本身不支持租户隔离,必须靠应用层强制加前缀 + 服务端校验,否则任意租户都能互相收发消息。
频道名必须带 tenant:{tenant_id}: 前缀
这是最基础、也最容易被绕过的防线。频道不是“建议”加前缀,而是“必须”加,且格式要统一、可解析、防冲突。
- ✅ 正确示例:
tenant:abc123:order_updated、tenant:xyz789:notification—— 语义清晰、租户唯一、便于 ACL 控制 - ❌ 错误示例:
abc123:order_updated(缺tenant:前缀,易与系统级频道如health:check冲突) - ⚠️ 特殊字符要编码:若
tenant_id含/、:或空格,必须用URLEncoder.encode(tenantId, "UTF-8")或 Base64 处理,否则 Redis 协议可能截断或报错ERR invalid channel name - ? 禁止用
user_id替代tenant_id:用户可跨租户存在,且粒度太细,无法支撑组织级权限管控
每次 SUBSCRIBE 和 PUBLISH 前必须校验频道合法性
只靠命名约定等于没隔离。真实生产环境里,恶意客户端或前端传参污染会直接绕过前缀规则,所以服务端必须在 Redis 操作前做白名单判断。
- 校验逻辑应基于当前请求上下文中的可信
tenant_id(来自 JWT claim、session 或数据库查出的租户元数据),而非直接取 URL 或 body 里的字段 - 收到
SUBSCRIBE tenant:def456:log时,若当前租户是abc123,应立即拒绝并返回 HTTP 403 或自定义错误码,不转发给 Redis -
PUBLISH同理:若请求携带channel=tenant:ghi789:alert,但当前上下文租户是abc123,必须拦截 —— 否则就变成租户 A 主动向租户 B “投毒” - 校验位置建议放在网关层或统一消息 SDK 中,避免每个业务模块重复写逻辑
ACL 配合频道前缀做二次防护
Redis 6+ 的 ACL 功能不能替代应用层校验,但能作为兜底手段,防止配置错误或绕过网关的直连行为。
- 为每个租户创建独立 Redis 用户,例如
user tenant_abc123 on >mypass ~tenant:abc123:* +subscribe +publish - 注意
~tenant:abc123:*中的波浪号表示 key pattern,它对频道(pub/sub channel)同样生效 —— 这是很多人忽略的关键点 - ACL 不支持正则,所以不能写
~tenant:*:*;必须为每个租户单独配,或用自动化脚本批量生成 - 启用 ACL 后,若客户端用错误用户连接并尝试
SUBSCRIBE tenant:def456:log,Redis 会直接返回NOAUTH Authentication required或NOPERM错误,不进入业务逻辑
不要依赖 PSUBSCRIBE 做租户路由
模式订阅(PSUBSCRIBE)看似灵活,但在多租户场景下极易引发越权和性能问题,应禁用或严格限制。
-
PSUBSCRIBE tenant:*:*表面看能覆盖所有租户频道,但实际会让一个租户监听到其他租户的消息 —— 只要频道名匹配 pattern,Redis 就全量推送 - 更危险的是
PSUBSCRIBE *,它等同于全局偷听,任何新频道都会被收到,完全破坏隔离边界 - 如果业务真需要泛订阅(比如审计中心),应走独立通道(如专用 audit 频道 + 单独 ACL 用户),而不是复用租户频道空间
- 生产环境建议在 Redis 配置中禁用
psubscribe命令:在redis.conf加rename-command PSUBSCRIBE ""
真正难的不是加前缀,而是让所有路径——从 API 入口、SDK 封装、网关策略到 Redis ACL——都对同一个 tenant_id 做一致校验。漏掉任意一环,隔离就形同虚设。


















