Mybatis Plus租户字段隔离查询所有租户数据方法

1、TenantLineInnerInterceptor

TenantLineInnerInterceptor类是mybatis plus中的租户拦截器,TenantLineInnerInterceptor的构造方法需要传入TenantLineHandler对象,所以得实现一下TenantLineHandler接口

    @Autowired
    private SecurityFrameworkService securityFrameworkService;
    
    @Bean
    public MyTenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties,
                                                                   MybatisPlusInterceptor interceptor) {

        MyTenantLineInnerInterceptor inner = new MyTenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties)
                ,properties,securityFrameworkService);
        MyBatisUtils.addInterceptor(interceptor, inner, 0);
        return inner;
    }
package cn.iocoder.yudao.framework.tenant.core.db;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.SimpleNode;
import net.sf.jsqlparser.schema.Column;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class TenantDatabaseInterceptor implements TenantLineHandler {

    private final Set<String> ignoreTables = new HashSet<>();

    public TenantDatabaseInterceptor(TenantProperties properties
    ) {
        properties.getIgnoreTables().forEach(table -> {
            ignoreTables.add(table.toLowerCase());
            ignoreTables.add(table.toUpperCase());
        });
        ignoreTables.add("DUAL");

    }

    @Override
    public Expression getTenantId() {
        return new LongValue(TenantContextHolder.getRequiredTenantId());

    }

    @Override
    public boolean ignoreTable(String tableName) {
        return TenantContextHolder.isIgnore() // 情况一,全局忽略多租户
                || CollUtil.contains(ignoreTables, tableName); // 情况二,忽略多租户的表
    }

}

getTenantId方法是获取当前上下文的租户Id,TenantLineInnerInterceptor中会将租户id带入到sql中,
ignoreTable 集合中是不区分租户的表,TenantLineInnerInterceptor中会判断如果不区分租户则不会在sql中加入租户id
在这里插入图片描述

2、重写TenantLineInnerInterceptor类的processSelect方法
实现关键在于当访问的账号是系统租户时,给查询语句增加一个条件,所以在重写TenantLineInnerInterceptor类时可以增加一下自己系统中的SecurityService,判断当前访问的是不是系统租户,当然也可以通过其他方式判断

MyTenantLineInnerInterceptor:

package cn.iocoder.yudao.framework.tenant.config;

import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import java.util.List;
import java.util.Set;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.WithItem;
public class MyTenantLineInnerInterceptor extends TenantLineInnerInterceptor {
    TenantProperties properties;

    SecurityFrameworkService securityFrameworkService;


    public MyTenantLineInnerInterceptor(final TenantLineHandler tenantLineHandler,TenantProperties properties
    ,SecurityFrameworkService securityFrameworkService) {
        super(tenantLineHandler);
        this.properties=properties;
        this.securityFrameworkService=securityFrameworkService;

    }

    protected void processSelect(Select select, int index, String sql, Object obj) {
        String whereSegment = (String) obj==null?"":(String) obj;
        Set<String> ignoreTables = properties.getIgnoreTables();
        if (select.getSelectBody() instanceof PlainSelect) {
            PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
            String mainTable=plainSelect.getFromItem().toString();
            Expression originalWhere = plainSelect.getWhere();
            //管理员看所有租户数据,但租户状态必须是正常状态
            if(!ignoreTables.contains(mainTable)&&securityFrameworkService.hasPermission("super:user")){
                Expression expression=null;
                try {
                    expression = CCJSqlParserUtil.parseCondExpression("tenant_id IN (SELECT id from ct.system_tenant st where st.status=0 and expire_time >now())");
                } catch (JSQLParserException e) {
                    throw new RuntimeException(e);
                }
                if (originalWhere == null) {
                    plainSelect.setWhere(expression);
                } else {
                    plainSelect.setWhere(new AndExpression(originalWhere, expression));
                }
            }
        }
        this.processSelectBody(select.getSelectBody(), whereSegment);
        List<WithItem> withItemsList = select.getWithItemsList();
        if (!CollectionUtils.isEmpty(withItemsList)) {
            withItemsList.forEach((withItem) -> {
                this.processSelectBody(withItem, whereSegment);
            });
        }
    }

}

3、系统租户如何操作其他租户数据

public static void execute(Long tenantId, Runnable runnable) {
        Long oldTenantId = TenantContextHolder.getTenantId();
        Boolean oldIgnore = TenantContextHolder.isIgnore();
        try {
            TenantContextHolder.setTenantId(tenantId);
            TenantContextHolder.setIgnore(false);
            // 执行逻辑
            runnable.run();
        } finally {
            TenantContextHolder.setTenantId(oldTenantId);
            TenantContextHolder.setIgnore(oldIgnore);
        }
    }
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值