当前位置: 首页 > news >正文

Spring Cache 多租户缓存隔离解决方案实践

Spring Boot 多租户缓存解决方案实践

在构建多租户 SaaS 应用时,确保不同租户数据隔离是至关重要的。Spring Cache 作为常用的缓存框架,在多租户场景下需要特殊处理以避免数据泄露和缓存污染。本文将分享一种通用的多租户缓存解决方案。

问题背景

在多租户系统中,所有租户共享同一套应用实例,但数据必须严格隔离。使用 Spring Cache 时,如果不做特殊处理,可能会出现以下问题:

  1. 不同租户的数据缓存到同一个 key 下,导致数据混乱
  2. 租户 A 查询的数据被租户 B 获取到,造成数据泄露
  3. 缓存失效时影响所有租户,而非仅影响特定租户

解决方案:自定义 CacheResolver

核心思路

通过自定义 CacheResolver,动态生成包含租户标识的缓存名称,从而实现租户间缓存的完全隔离。

实现代码

@Component("tenantCacheResolver")
public class GenericTenantCacheResolver implements CacheResolver {@Autowiredprivate CacheManager cacheManager;@Overridepublic Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {// 从方法注解中获取缓存名称模板String cacheNameTemplate = getCacheNameTemplate(context);// 获取当前租户IDString tenantId = getCurrentTenantId();String finalCacheName = cacheNameTemplate.replace("{tenant}", tenantId != null ? tenantId : "default");Cache cache = cacheManager.getCache(finalCacheName);return Collections.singletonList(cache);}private String getCacheNameTemplate(CacheOperationInvocationContext<?> context) {Cacheable cacheable = context.getMethod().getAnnotation(Cacheable.class);if (cacheable != null && cacheable.value().length > 0) {return cacheable.value()[0];}return "default";}private String getCurrentTenantId() {// 根据实际项目情况获取租户ID// 可能是从 SecurityContext、ThreadLocal 或其他上下文获取return TenantContext.getCurrentTenantId();}
}

使用示例

@Service
public class BudgetItemsService {@Cacheable(cacheResolver = "tenantCacheResolver", value = "budgetItems:list:{tenant}", key = "#itemName ?: 'default'", unless = "#result.isEmpty()")public List<BudgetItems> getList(String itemName) {// 业务逻辑return budgetItemsMapper.selectList(wrapper);}@Cacheable(cacheResolver = "tenantCacheResolver", value = "user:info:{tenant}", key = "#userId", unless = "#result == null")public User getUserInfo(Long userId) {// 业务逻辑return userMapper.selectById(userId);}
}

高级版本:支持多个缓存

@Component("tenantCacheResolver")
public class AdvancedTenantCacheResolver implements CacheResolver {@Autowiredprivate CacheManager cacheManager;@Overridepublic Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {String tenantId = getCurrentTenantId();String tenantSuffix = tenantId != null ? tenantId : "default";List<Cache> caches = new ArrayList<>();String[] cacheNames = getCacheNames(context);for (String cacheName : cacheNames) {String finalCacheName = cacheName.replace("{tenant}", tenantSuffix);Cache cache = cacheManager.getCache(finalCacheName);if (cache != null) {caches.add(cache);}}return caches;}private String[] getCacheNames(CacheOperationInvocationContext<?> context) {Cacheable cacheable = context.getMethod().getAnnotation(Cacheable.class);if (cacheable != null) {return cacheable.value();}return new String[]{"default"};}private String getCurrentTenantId() {// 实际实现根据项目情况而定return SecurityUtils.getTenantId();}
}

优势分析

  1. 通用性强:一个 CacheResolver 可以处理所有需要租户隔离的缓存场景
  2. 配置简单:只需在 @Cacheable 的 value 中使用 {tenant} 占位符
  3. 维护方便:租户逻辑集中在一个地方处理
  4. 兼容性好:自动处理租户ID为null的情况
  5. 扩展性佳:可以轻松添加其他维度的缓存隔离(如用户、角色等)

注意事项

  1. 确保租户ID获取逻辑的正确性和性能
  2. 在租户ID为null时提供默认值,避免缓存键为空
  3. 合理设计缓存名称,避免过长或特殊字符
  4. 监控缓存使用情况,避免缓存膨胀

总结

通过自定义 CacheResolver 实现多租户缓存隔离,是一种优雅且实用的解决方案。它不仅解决了多租户场景下的缓存隔离问题,还保持了代码的简洁性和可维护性。这种方案可以广泛应用于各种多租户 SaaS 应用中,为系统提供安全可靠的缓存机制。


文章转载自:

http://kawYQ5US.cLpfd.cn
http://rxfr2LRn.cLpfd.cn
http://HxV6rxAU.cLpfd.cn
http://st1FrCTY.cLpfd.cn
http://MLfBHtX3.cLpfd.cn
http://ahqJiqxE.cLpfd.cn
http://Wa5nwAX9.cLpfd.cn
http://UQXVORVf.cLpfd.cn
http://twtaOLma.cLpfd.cn
http://ZyN1zGvF.cLpfd.cn
http://MtxdeHuJ.cLpfd.cn
http://lQuHl4un.cLpfd.cn
http://oQXoeVwS.cLpfd.cn
http://RJuNc55S.cLpfd.cn
http://9P2NXdmQ.cLpfd.cn
http://DAhwftet.cLpfd.cn
http://023pVW7x.cLpfd.cn
http://aoxwYa0P.cLpfd.cn
http://65pw8Dcq.cLpfd.cn
http://ci3OJtua.cLpfd.cn
http://GME5t0IX.cLpfd.cn
http://R7zPYIYM.cLpfd.cn
http://tA5xZ6Mm.cLpfd.cn
http://Zn3g0t17.cLpfd.cn
http://4V4K2b1U.cLpfd.cn
http://Ah4fXqh9.cLpfd.cn
http://RHIoKEU7.cLpfd.cn
http://pnch4shO.cLpfd.cn
http://qPk8Ni56.cLpfd.cn
http://Hf2jQ9qi.cLpfd.cn
http://www.dtcms.com/a/374795.html

相关文章:

  • Mybatis-12 第三方缓存-EhCache
  • 【C++】特别的程序错误处理方式——异常机制
  • 嵌入式设备上mqtt库的使用
  • 【Linux基础知识系列:第一百二十六篇】使用dd命令进行磁盘复制
  • 从零到一使用Linux+Nginx+MySQL+PHP搭建的Web网站服务器架构环境——LNMP(上)
  • 使用虚拟机Ubuntu搭建mosquito服务器 使esp32、上位机通信
  • 云计算技术栈
  • 国产时序数据库选型指南-从大数据视角看透的价值
  • 东京本社招聘 | 财务负责人 多个日本IT岗位(Java/C++/Python/AWS 等),IT营业同步招募
  • AWS ALB 详解:智能流量调度器
  • Django REST框架:ModelViewSet全面解析
  • 基于Centos7.9搭建svn服务端
  • PyTorch 和nn.Conv2d详解
  • pytorch基本运算-分离计算
  • 基于容器化云原生的 MySQL 及中间件高可用自动化集群项目
  • “图观”端渲染场景编辑器
  • 构建分布式京东商品数据采集系统:基于 API 的微服务实现方案
  • HTML5点击转圈圈 | 深入了解HTML5技术中的动态效果与用户交互设计
  • springboot rabbitmq 延时队列消息确认收货订单已完成
  • CString(MFC/ATL 框架)和 QString(Qt 框架)
  • Sklearn(机器学习)实战:鸢尾花数据集处理技巧
  • 工具框架:Scikit-learn、Pandas、NumPy预测鸢尾花的种类
  • AI GEO 优化能否快速提升网站在搜索引擎的排名?​
  • nvm和nrm的详细安装配置,从卸载nodejs到安装NVM管理nodejs版本,以及安装nrm管理npm版本
  • 对口型视频怎么制作?从脚本到成片的全流程解析
  • 从“能说话”到“会做事”:AI Agent如何重构日常工作流?
  • 洛谷 P1249 最大乘积-普及/提高-
  • 小红书获取笔记详情API接口会返回哪些数据?
  • JAVA Spring Boot maven导入使用本地SDK(jar包)
  • Linux/UNIX系统编程手册笔记:SOCKET