› 首页 ›未命名›正文

消息未读数量查询使用缓存时怎样避免混入其他用户的数据?

admin 未命名 2026-10-20 17:08:12 12 0
消息未读数量查询使用缓存时,应将用户ID、租户ID等身份标识纳入缓存键,确保不同用户的数据独立存储,避免使用公共键,查询前需校验登录状态与访问权限,用户身份应从可信会话中获取,不可直接信任客户端参数,数据库查询和缓存回源必须采用一致的用户过滤条件,写入缓存前确认数据归属,并在消息已读、删除等操作后及时更新或失效缓存,防止越权读取与数据串用。

未读消息数用了缓存,为什么会串到别人账号?这几道隔离防线必须做好

打开消息中心,头像旁边明明应该显示“3”,结果却跳成了“99”;切换账号后,上一位用户的未读数量还留在页面上,这类问题看似只是一个数字显示错误,背后却可能暴露出缓存隔离、身份校验甚至数据访问权限方面的漏洞。

消息未读数量查询通常调用频繁,非常适合引入 Redis 等缓存来减轻数据库压力,但缓存不能只追求“查得快”,更要保证“拿得对”,想避免混入其他用户的数据,需要把用户身份、缓存键、查询条件和前端状态一起管起来。

缓存键不能只写业务名称

最常见的错误,是所有用户共用一个缓存键:

message:unread:count

用户甲查询时写入“12”,用户乙随后查询,直接命中这个键,自然就拿到了甲的数据。

正确的做法,是让缓存键包含完整的数据归属信息,

prod:app01:tenant:1001:user:9527:message:unread:v1

这里分别体现了运行环境、应用、租户、用户和业务版本,如果系统不存在租户概念,可以省略租户字段;但在多租户系统中,仅有用户 ID 未必足够,尤其是用户编号只在各租户内部唯一时。

如果未读数量还区分消息类型、工作空间或频道,这些会影响结果的维度也应进入缓存键。判断一个键是否安全,不是看它够不够长,而是看它能否唯一描述这份数据属于谁、统计什么范围。

用户身份必须由服务端确认

即使缓存键包含用户 ID,也不意味着安全,如果接口允许客户端随便传入一个 userId,服务端未经授权校验便拼接缓存键,攻击者仍可能读取其他用户的未读数量。

对于“查询我的未读数”这种接口,用户身份应从已经验证的登录会话或访问令牌中获取,而不是直接相信请求参数,租户身份也要结合当前用户的成员关系和访问权限确定,不能只相信前端传来的租户编号。

如果管理员确实需要查询他人数据,应使用独立的授权逻辑,明确校验可访问范围,并记录必要的审计日志,缓存命中只是读取路径的一环,不能成为跳过权限检查的理由。

缓存未命中时,数据库查询也要隔离

不少团队只检查缓存键,却忽略了回源查询。

假设缓存键写得很规范,但数据库执行的是:

SELECT COUNT(*)
FROM messages
WHERE is_read = false;

得到的依然是全局未读总数,然后还会被写进某个用户的缓存中。

对于按接收人记录消息的简单表结构,查询至少需要类似这样的条件:

SELECT COUNT(*)
FROM messages
WHERE tenant_id = :tenant_id
  AND recipient_id = :user_id
  AND is_read = false;

参数应来自经过验证的身份上下文,并使用参数化查询,若系统采用消息表、接收关系表和阅读状态表分离的设计,还要检查关联条件是否完整,避免把其他接收人的记录一起统计进去。

消息未读数量查询使用缓存时怎样避免混入其他用户的数据?
(图片来源网络,侵删)

别把“数据串号”和“数量不准”混为一谈

新增消息、标记已读、批量清空未读,都可能改变缓存中的数字,如果更新顺序不当,用户可能看到旧值;如果事件重复消费,计数还可能重复增加或减少。

这些通常属于一致性问题,不一定是用户隔离出了问题。

消息未读数量查询使用缓存时怎样避免混入其他用户的数据?
(图片来源网络,侵删)

实现上可以选择数据库提交后删除对应用户缓存,下次读取时重新统计;也可以采用事件驱动更新,但必须处理事件重复、乱序和失败重试,需要注意,删除缓存并不能天然解决所有并发竞争,严格一致的场景还要结合版本校验、串行化处理或可靠事件机制设计。

TTL 只能限制旧数据存在的时间,不能修复错误的缓存键,更不能替代权限控制。

前端缓存和网关缓存同样要检查

后端隔离正确,页面仍然可能显示别人的数字,典型原因包括:切换账号后没有清空状态管理中的未读数,浏览器本地存储使用了固定键,或者旧账号发出的请求在退出后才返回,又覆盖了新账号的页面状态。

退出登录、切换账号和切换租户时,应清理相关缓存,并对异步响应校验当前身份是否仍与发起请求时一致。

个性化未读接口也不应直接进入共享 CDN 或网关缓存,没有明确设计身份隔离规则时,可以采用 Cache-Control: private, no-store 等策略,避免响应被共享或存储;服务端内部 Redis 缓存则仍可按业务规则独立使用。

服务器是基础,权限隔离才是关键

未读数接口在用户量增长后,会持续消耗应用、数据库和缓存资源,基础设施容量与稳定性也需要提前规划,如果正在选购业务服务器,可以把慈云数据加入候选清单,根据实际业务咨询 CPU、内存、带宽、部署区域及备份方案,并通过压测和服务条款核实是否符合需求。

购买服务器不能替代安全设计,无论选择哪家服务商,Redis 都应避免直接暴露在公网,并按部署条件配置网络访问限制、认证、ACL 和传输保护,生产与测试环境也应明确隔离,防止误连、误写。

上线前,用双账号测试验证边界

最有效的检查方式,是准备两个未读数量明显不同的账号,例如甲为“2”、乙为“37”,反复执行登录、退出、切换账号、切换租户和并发查询。

测试既要覆盖缓存命中,也要覆盖缓存未命中;还要尝试篡改请求中的用户或租户参数,确认服务端不会越权读取,条件允许时,可以在缓存值中附带用户、租户和统计版本等元信息,读取时进行一致性检查,作为额外防线,但不能用它替代正确的键设计和授权逻辑。

归根结底,避免未读消息缓存串号,需要守住三个基本原则:身份可信、键空间隔离、回源条件完整。再配合前端状态清理、共享缓存限制和系统化测试,才能让这个看似简单的小红点,既响应迅速,也守得住用户之间的数据边界。

用户唯一标识
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

广州AI科技日报

https://rtximg.cn/

统计代码 | 京ICP备100000000

Powered By 某某网络科技有限公司

简体中文English