目录
0. 前言
上一篇文章中,我们一起了解了一下saas系统架构中实现租户数据隔离的解决方案。本文中我们就来自己实现其中的一种解决方案,即使用租户id字段来实现同一张数据表中不同租户数据的增删改查。
我们将使用 springboot + mybatis-plus 这套组合框架,写一个小小的demo来演示如何实现这个方案。
事先声明,本文仅说明如何实现多租户的数据隔离,并不展开讨论其他问题,例如登录后会话有效期,超时后会话清理,数据库集群环境下的数据同步等。
嫌看文章麻烦啰嗦的大神,可以直接去看本文所涉及的代码。下面是码云仓库地址
https://gitee.com/zectorlion/MultiTenancy
仓库中的Solution3项目既是本文相关的代码(带sql脚本哦)
1. why mybatis-plus?
之所以使用 springboot + mybatis-plus 这套组合框架,除了他们是现在用的比较多的框架这个原因外,最重要的原因是,mybatis-plus框架已经给我们提供了现成的多租户数据crud解决方案。如果大家经常关注mybatis-plus的官方github仓库,那大家一定会注意到这么一个maven项目
这个项目就是mybatis-plus提供给我们的,使用租户id字段解决同一张数据表中不同租户数据的crud问题的。
既然已经有轮子了,那我们就不需要再重复造轮子了,这样能节省我们好多功夫。感谢mybatis-plus开发小组,这里必须给你们点32个赞。
我们展开这个mybatis-plus-sample-tenant项目,在com.baomidou.mybatisplus.samples.tenant.config这个包下面有一个MybatisPlusConfig配置类。mybatis-plus解决多租户数据crud问题的方法,就在这个类的paginationInterceptor方法中。在paginationInterceptor方法中,创建了一个处理多租户sql语句的TenantSqlParser,而这个TenantSqlParser又通过调用setTenantHandler方法指定一个TenantHandler,这个TenantHandler是在mybatis-plus实际进行sql语句改写之前,为mybatis-plus指定租户id字段名称,租户id的值以及判断是否进行sql改写的处理器。
在TenantHandler中一共就三个方法,getTenantId,getTenantIdColumn,doTableFilter。
getTenantId方法用于在改写sql之前设置租户id,getTenantIdColumn方法用于指定数据表中区分租户的字段。通过这两个方法,mybatis-plus就可以改写sql语句,管理某个租户的数据。例如getTenantId方法返回1,getTenantIdColumn方法返回tenant_id,那么在执行select语句的时候,就会增加一个 tenant_id=1 的where条件。
doTableFilter方法用于指定不进行多租户处理的表,例如用户信息表。
由此可知,我们只需要编写一个我们自己的TenantHandler,就可以实现通过租户id字段在单个数据表中区分租户数据的功能了。
2. 需求分析
我们整个项目将为用户提供两个对外的api接口,它们分别是
- 用户登录接口:接口访问地址是/user/login/{id},用户将自己的id号传递给接口进行登录。为了避免用户在登录时,租户id被抓包工具窃取,造成安全隐患,所以我们在用户登录时为用户生成一个 token 并返回给用户,然后将 token 和该用户的租户id关联,放入一个叫 tenantContext 的对象中。tenantContext 对象是用于保存用户 token 和租户id的关联关系的工具类,它将在项目启动时被注入到 springIOC 容器中。
- 资料数据增删改查接口:接口访问地址是/profile/findAll/{token}、/profile/add/{token}等。用户调用接口时,必须携带其登录时系统返回给他的 token。系统将根据用户传递的token,从 tenantContext 对象中取出用户的租户id,然后将该租户id传递给 mybatis-plus ,让mybatis-plus使用sql生成器生成特定租户的sql并执行,从而完成租户数据的增删改查。
OK,确定好了我们要干的“活”,下面我们就正式开工。
3. 环境准备
在开发任何系统之前,我们要先把环境准备好,所谓“工欲善其事,必先利其器”嘛。
我先给大家交代一下我所使用的系统环境和对应的版本,避免大家在版本号的问题上踩坑。
- springboot:2.1.4.RELEASE
- mybatis-plus:3.0.5
- mysql数据库:5.7.26-log MySQL Community Server (GPL)
- 谷歌浏览器:76.0.3809.132(正式版本) (64 位)
首先我们先准备一些测试数据,就是建个库,再建两张表,导入点数据。这两张表分别是存放用户信息的user表,和存放资料信息的profile表。
其中user表中有6条用户数据,分别属于两个不同的租户
profile表中有两条数据,也分别属于不同的租户
使用下面的sql脚本,可以直接完成建库,建表和导数据的整个过程。
-- Dump c