数据逻辑隔离

1、需求

对已有服务进行逻辑隔离,按照请求头中租户标志进行区分,采用逻辑隔离方式,在对应表中增加租户字段,执行新增、查询、删除sql语句时携带该字段

2、方案

采用mybatis框架自带的TenantLineInnerInterceptor插件,

可以实现插入、查询时自动携带租户字段,并且支持灵活配置

步骤一

yml租户配置

tenant:
  # 是否开启租户模式
  enable: true
  # 需要排除的多租户的表
  exclusionTable:
    - "t_test_log"
    - "t_test_user"
  # 租户字段名称
  column: tenant_id

步骤二

除了已排除的表,其余表增加租户标志

步骤三

租户配置

@Data
@Configuration
@ConfigurationProperties(prefix = "tenant")
public class TenantProperties {

    /**
     * 是否开启租户模式
     */
    private Boolean enable;

    /**
     * 多租户字段名称
     */
    private String column;

    /**
     * 需要排除的多租户的表
     */
    private List<String> exclusionTable;

}

租户插件配置

@Configuration
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@AutoConfigureBefore(MybatisPlusConfig.class)
public class TenantConfig {

    private final TenantProperties tenantProperties;

    /**
     * 新多租户插件配置,一缓和二缓遵循mybatis的规则,
     * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
     * 避免缓存万一出现问题
     *
     * @return TenantLineInnerInterceptor
     */
    @Bean
    public TenantLineInnerInterceptor tenantLineInnerInterceptor() {
        return new TenantLineInnerInterceptor(new TenantLineHandler() {
            /**
             * 获取租户ID
             * @return Expression
             */
            @Override
            public Expression getTenantId() {
                String tenant = TenementFlagContextHolder.getTenementFlag();
                if (tenant != null) {
                    return new StringValue(TenementFlagContextHolder.getTenementFlag());
                }
                return new NullValue();
            }

            /**
             * 获取多租户的字段名
             * @return String
             */
            @Override
            public String getTenantIdColumn() {
                return tenantProperties.getColumn();
            }

            /**
             * 过滤不需要根据租户隔离的表
             * 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
             * @param tableName 表名
             */
            @Override
            public boolean ignoreTable(String tableName) {
                return tenantProperties.getExclusionTable().stream().anyMatch(
                        (t) -> t.equalsIgnoreCase(tableName)
                );
            }
        });
    }
}

mybatis配置,根据配置开启租户插件

@Configuration
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@MapperScan({"com.taoge.**"})
public class MybatisPlusConfig {

    private final TenantLineInnerInterceptor tenantLineInnerInterceptor;

    private final TenantProperties tenantProperties;

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false
     * 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {

        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        //多租户插件
        if (tenantProperties.getEnable()) {
            interceptor.addInnerInterceptor(tenantLineInnerInterceptor);
        }

        //分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        //防止全表更新与删除插件: BlockAttackInnerInterceptor
        BlockAttackInnerInterceptor blockAttackInnerInterceptor = new BlockAttackInnerInterceptor();
        interceptor.addInnerInterceptor(blockAttackInnerInterceptor);

        return interceptor;
    }

    /**
     * 乐观锁插件 当要更新一条记录的时候,希望这条记录没有被别人更新
     * https://mybatis.plus/guide/interceptor-optimistic-locker.html#optimisticlockerinnerinterceptor
     */
    @Bean
    public OptimisticLockerInnerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInnerInterceptor();
    }

}

results matching ""

    No results matching ""