import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
import org.reflections.scanners.Scanner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ReflectionUtils;
public class EntityDataTenantHolder implements DataTenantHolder {
private Map<String, Boolean> entityBooleanMap = new HashMap(500);
private Map<String, Boolean> entityTenantBooleanMap = new HashMap(500);
@Autowired
private DataTenantConfig dataTenantConfig;
public EntityDataTenantHolder() {
}
@PostConstruct
public void init() {
if (StringUtils.isNotBlank(this.dataTenantConfig.getModelPackage())) {
String[] modelPackStrs = this.dataTenantConfig.getModelPackage().split(",");
String[] var2 = modelPackStrs;
int var3 = modelPackStrs.length;
for(int var4 = 0; var4 < var3; ++var4) {
String modelPackStr = var2[var4];
Reflections reflections = new Reflections(modelPackStr, new Scanner[0]);
Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(TableName.class);
classSet.forEach((cls) -> {
TableName tableName = (TableName)cls.getAnnotation(TableName.class);
if (Objects.nonNull(tableName)) {
if (ReflectionUtils.findField(cls, this.dataTenantConfig.getDataTenantIdField()) != null) {
this.entityBooleanMap.put(tableName.value(), true);
} else {
this.entityBooleanMap.put(tableName.value(), false);
}
if (ReflectionUtils.findField(cls, this.dataTenantConfig.getTenantIdField()) != null) {
this.entityTenantBooleanMap.put(tableName.value(), true);
} else {
this.entityTenantBooleanMap.put(tableName.value(), false);
}
}
});
}
} else {
List<TableInfo> tableInfos = TableInfoHelper.getTableInfos();
if (CollectionUtils.isNotEmpty(tableInfos)) {
Iterator var9 = tableInfos.iterator();
while(var9.hasNext()) {
TableInfo tableInfo = (TableInfo)var9.next();
if (ReflectionUtils.findField(tableInfo.getEntityType(), this.dataTenantConfig.getDataTenantIdField()) != null) {
this.entityBooleanMap.put(tableInfo.getTableName(), true);
} else {
this.entityBooleanMap.put(tableInfo.getTableName(), false);
}
if (ReflectionUtils.findField(tableInfo.getEntityType(), this.dataTenantConfig.getTenantIdField()) != null) {
this.entityTenantBooleanMap.put(tableInfo.getTableName(), true);
} else {
this.entityTenantBooleanMap.put(tableInfo.getTableName(), false);
}
}
}
}
LogTools.info("初始化需要租户的实体类:{}", new Object[]{JSON.toJSONString(this.entityBooleanMap)});
}
public boolean containEntityMap(String databaseName, String tableName) {
if (StringUtils.isNotBlank(databaseName)) {
String fullName = databaseName + "." + tableName;
return this.entityBooleanMap.get(fullName) != null ? (Boolean)this.entityBooleanMap.get(fullName) : false;
} else {
return this.entityBooleanMap.get(tableName) != null ? (Boolean)this.entityBooleanMap.get(tableName) : false;
}
}
public boolean containEntityTenantMap(String databaseName, String tableName) {
if (StringUtils.isNotBlank(databaseName)) {
String fullName = databaseName + "." + tableName;
return this.entityTenantBooleanMap.get(fullName) != null ? (Boolean)this.entityTenantBooleanMap.get(fullName) : false;
} else {
return this.entityTenantBooleanMap.get(tableName) != null ? (Boolean)this.entityTenantBooleanMap.get(tableName) : false;
}
}
}
import com.baomidou.mybatisplus.core.parser.AbstractJsqlParser;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.LateralSubSelect;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.select.SubJoin;
import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.statement.select.ValuesList;
import net.sf.jsqlparser.statement.select.WithItem;
import net.sf.jsqlparser.statement.update.Update;
public class DataTenantSqlParser extends AbstractJsqlParser {
private DataTenantHandler dataTenantHandler;
public void processSelectBody(SelectBody selectBody) {
if (selectBody instanceof PlainSelect) {
this.processPlainSelect((PlainSelect)selectBody);
} else if (selectBody instanceof WithItem) {
WithItem withItem = (WithItem)selectBody;
if (withItem.getSelectBody() != null) {
this.processSelectBody(withItem.getSelectBody());
}
} else {
SetOperationList operationList = (SetOperationList)selectBody;
if (operationList.getSelects() != null && operationList.getSelects().size() > 0) {
operationList.getSelects().forEach(this::processSelectBody);
}
}
}
public void processInsert(Insert insert) {
Table table = insert.getTable();
if (!this.dataTenantHandler.doTableFilter(table.getSchemaName(), table.getName())) {
Expression expression = this.dataTenantHandler.getDataTenantId(false, "insert");
if (!Objects.isNull(expression)) {
List<Column> insertColumns = insert.getColumns();
Iterator var5 = insertColumns.iterator();
Column insertColumn;
do {
if (!var5.hasNext()) {
insert.getColumns().add(new Column(this.dataTenantHandler.getDataTenantIdColumn()));
if (insert.getSelect() != null) {
this.processPlainSelect((PlainSelect)insert.getSelect().getSelectBody(), true);
} else {
if (insert.getItemsList() == null) {
throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId", new Object[0]);
}
ItemsList itemsList = insert.getItemsList();
if (itemsList instanceof MultiExpressionList) {
((MultiExpressionList)itemsList).getExprList().forEach((el) -> {
el.getExpressions().add(this.dataTenantHandler.getDataTenantId(false, "insert"));
});
} else {
((ExpressionList)insert.getItemsList()).getExpressions().add(this.dataTenantHandler.getDataTenantId(false, "insert"));
}
}
return;
}
insertColumn = (Column)var5.next();
} while(!Objects.equals(insertColumn.getColumnName(), this.dataTenantHandler.getDataTenantIdColumn()));
}
}
}
public void processUpdate(Update update) {
}
public void processDelete(Delete delete) {
Table table = delete.getTable();
if (!this.dataTenantHandler.doTableFilter(table.getSchemaName(), table.getName())) {
Expression expression = this.dataTenantHandler.getDataTenantId(true, "where");
if (!Objects.isNull(expression)) {
delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere()));
}
}
}
protected BinaryExpression andExpression(Table table, Expression where) {
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(this.getAliasColumn(table));
equalsTo.setRightExpression(this.dataTenantHandler.getDataTenantId(true, "where"));
if (null != where) {
return where instanceof OrExpression ? new AndExpression(equalsTo, new Parenthesis(where)) : new AndExpression(equalsTo, where);
} else {
return equalsTo;
}
}
protected void processPlainSelect(PlainSelect plainSelect) {
this.processPlainSelect(plainSelect, false);
}
protected void processPlainSelect(PlainSelect plainSelect, boolean addColumn) {
FromItem fromItem = plainSelect.getFromItem();
if (fromItem instanceof Table) {
Table fromTable = (Table)fromItem;
if (!this.getDataTenantHandler().doTableFilter(fromTable.getSchemaName(), fromTable.getName()) && !Objects.isNull(this.dataTenantHandler.getDataTenantId(false, "select"))) {
plainSelect.setWhere(this.builderExpression(plainSelect.getWhere(), fromTable));
if (addColumn) {
plainSelect.getSelectItems().add(new SelectExpressionItem(new Column(this.getDataTenantHandler().getDataTenantIdColumn())));
}
}
} else {
this.processFromItem(fromItem);
}
List<Join> joins = plainSelect.getJoins();
if (joins != null && joins.size() > 0) {
joins.forEach((j) -> {
this.processJoin(j);
this.processFromItem(j.getRightItem());
});
}
}
protected void processFromItem(FromItem fromItem) {
if (fromItem instanceof SubJoin) {
SubJoin subJoin = (SubJoin)fromItem;
if (subJoin.getJoinList() != null) {
subJoin.getJoinList().forEach(this::processJoin);
}
if (subJoin.getLeft() != null) {
this.processFromItem(subJoin.getLeft());
}
} else if (fromItem instanceof SubSelect) {
SubSelect subSelect = (SubSelect)fromItem;
if (subSelect.getSelectBody() != null) {
this.processSelectBody(subSelect.getSelectBody());
}
} else if (fromItem instanceof ValuesList) {
this.logger.debug("Perform a subquery, if you do not give us feedback");
} else if (fromItem instanceof LateralSubSelect) {
LateralSubSelect lateralSubSelect = (LateralSubSelect)fromItem;
if (lateralSubSelect.getSubSelect() != null) {
SubSelect subSelect = lateralSubSelect.getSubSelect();
if (subSelect.getSelectBody() != null) {
this.processSelectBody(subSelect.getSelectBody());
}
}
}
}
protected void processJoin(Join join) {
if (join.getRightItem() instanceof Table) {
Table fromTable = (Table)join.getRightItem();
if (this.dataTenantHandler.doTableFilter(fromTable.getSchemaName(), fromTable.getName()) || Objects.isNull(this.dataTenantHandler.getDataTenantId(false, "select"))) {
return;
}
join.setOnExpression(this.builderExpression(join.getOnExpression(), fromTable));
}
}
protected Expression builderExpression(Expression currentExpression, Table table) {
ItemsList itemsList = this.getDataTenantHandler().getSelectDataTenantId(false, "select");
Object appendExpression;
if (!(itemsList instanceof SupportsOldOracleJoinSyntax)) {
appendExpression = new InExpression();
((InExpression)appendExpression).setLeftExpression(this.getAliasColumn(table));
((InExpression)appendExpression).setRightItemsList(itemsList);
} else {
appendExpression = this.processTableAlias4CustomizedTenantIdExpression(new InExpression(this.getAliasColumn(table), itemsList), table);
}
if (currentExpression == null) {
return (Expression)appendExpression;
} else {
if (currentExpression instanceof BinaryExpression) {
BinaryExpression binaryExpression = (BinaryExpression)currentExpression;
this.doExpression(binaryExpression.getLeftExpression());
this.doExpression(binaryExpression.getRightExpression());
} else if (currentExpression instanceof InExpression) {
InExpression inExp = (InExpression)currentExpression;
ItemsList rightItems = inExp.getRightItemsList();
if (rightItems instanceof SubSelect) {
this.processSelectBody(((SubSelect)rightItems).getSelectBody());
}
}
return currentExpression instanceof OrExpression ? new AndExpression(new Parenthesis(currentExpression), (Expression)appendExpression) : new AndExpression(currentExpression, (Expression)appendExpression);
}
}
protected void doExpression(Expression expression) {
if (expression instanceof FromItem) {
this.processFromItem((FromItem)expression);
} else if (expression instanceof InExpression) {
InExpression inExp = (InExpression)expression;
ItemsList rightItems = inExp.getRightItemsList();
if (rightItems instanceof SubSelect) {
this.processSelectBody(((SubSelect)rightItems).getSelectBody());
}
}
}
protected Expression processTableAlias4CustomizedTenantIdExpression(Expression expression, Table table) {
return expression;
}
protected Column getAliasColumn(Table table) {
StringBuilder column = new StringBuilder();
if (null == table.getAlias()) {
column.append(table.getName());
} else {
column.append(table.getAlias().getName());
}
column.append(".");
column.append(this.dataTenantHandler.getDataTenantIdColumn());
return new Column(column.toString());
}
public DataTenantSqlParser() {
}
public DataTenantHandler getDataTenantHandler() {
return this.dataTenantHandler;
}
public DataTenantSqlParser setDataTenantHandler(DataTenantHandler dataTenantHandler) {
this.dataTenantHandler = dataTenantHandler;
return this;
}
public String toString() {
return "DataTenantSqlParser(dataTenantHandler=" + this.getDataTenantHandler() + ")";
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof DataTenantSqlParser)) {
return false;
} else {
DataTenantSqlParser other = (DataTenantSqlParser)o;
if (!other.canEqual(this)) {
return false;
} else if (!super.equals(o)) {
return false;
} else {
Object this$dataTenantHandler = this.getDataTenantHandler();
Object other$dataTenantHandler = other.getDataTenantHandler();
if (this$dataTenantHandler == null) {
if (other$dataTenantHandler != null) {
return false;
}
} else if (!this$dataTenantHandler.equals(other$dataTenantHandler)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof DataTenantSqlParser;
}
public int hashCode() {
int PRIME = true;
int result = super.hashCode();
Object $dataTenantHandler = this.getDataTenantHandler();
result = result * 59 + ($dataTenantHandler == null ? 43 : $dataTenantHandler.hashCode());
return result;
}
}
public class PreDataTenantHandler implements DataTenantHandler {
private static final Logger log = LoggerFactory.getLogger(PreDataTenantHandler.class);
@Autowired
private DataTenantConfig dataTenantConfig;
@Autowired
private DataTenantHolder dataTenantHolder;
public PreDataTenantHandler() {
}
public Expression getDataTenantId(boolean where, String type) {
String dataTenantId = "";
if ("insert".equals(type)) {
dataTenantId = ContextUtil.getDataTenantId();
} else {
dataTenantId = ContextUtil.getQueryDataTenantId();
}
log.debug("当前租户dataTenantId为{}", dataTenantId);
return StringUtils.isBlank(dataTenantId) ? null : new StringValue(String.format("'%s'", dataTenantId));
}
public ItemsList getSelectDataTenantId(boolean where, String type) {
String dataTenantId = "";
if ("insert".equals(type)) {
dataTenantId = ContextUtil.getDataTenantId();
} else {
dataTenantId = ContextUtil.getQueryDataTenantId();
}
log.debug("当前租户dataTenantId为{}", dataTenantId);
if (StringUtils.isBlank(dataTenantId)) {
return null;
} else {
Set<String> deptIds = Sets.newLinkedHashSet();
deptIds.add(dataTenantId);
deptIds.add("global");
ItemsList itemsList = new ExpressionList((List)deptIds.stream().map(StringValue::new).collect(Collectors.toList()));
return itemsList;
}
}
public String getDataTenantIdColumn() {
return this.dataTenantConfig.getDataTenantIdColumn();
}
public boolean doTableFilter(String databaseName, String tableName) {
if (this.dataTenantHolder.containEntityMap(databaseName, tableName) && !StringUtils.isBlank(this.dataTenantConfig.getDataTenantIdField())) {
if (StringUtils.isNotBlank(this.dataTenantConfig.getIgnoreTable())) {
if (StringUtils.isNotBlank(databaseName)) {
String fullName = databaseName + "." + tableName;
return Arrays.asList(this.dataTenantConfig.getIgnoreTable().split(",")).stream().anyMatch((e) -> {
return e.equalsIgnoreCase(fullName);
});
} else {
return Arrays.asList(this.dataTenantConfig.getIgnoreTable().split(",")).stream().anyMatch((e) -> {
return e.equalsIgnoreCase(tableName);
});
}
} else {
return false;
}
} else {
return true;
}
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
@RefreshScope
@Data
public class DataTenantConfig {
private static final Logger log = LoggerFactory.getLogger(DataTenantConfig.class);
@Value("${dataTenant.column.name:data_tenant_id}")
private String dataTenantIdColumn;
@Value("${dataTenant.column.field:dataTenantId}")
private String dataTenantIdField;
@Value("${dataTenant.column.tenantField:tenantId}")
private String tenantIdField;
@Value("${dataTenant.column.tenantName:tenant_id}")
private String tenantIdColumn;
@Value("${dataTenant.table.ignore:}")
private String ignoreTable;
@Value("${dataTenant.table.tenantIgnore:}")
private String tenantIgnoreTable;
@Value("${dataTenant.table.package:}")
private String modelPackage;
@Value("${dataTenant.mapper.use:}")
private String scanMapper;
@Configuration
@ConditionalOnProperty(
name = {"boot.tenant.enable"},
havingValue = "true"
)
public class MybatisTenantAutoConfigure {
private static final Logger log = LoggerFactory.getLogger(MybatisTenantAutoConfigure.class);
@Value("${spring.profiles.active}")
private String env;
public MybatisTenantAutoConfigure() {
}
@Bean
@ConditionalOnMissingBean({PaginationInterceptor.class})
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
@Bean
@ConditionalOnMissingBean({DataTenantSqlParser.class})
public DataTenantSqlParser dataTenantSqlParser(PaginationInterceptor paginationInterceptor, PreDataTenantHandler preDataTenantHandler) {
List<ISqlParser> sqlParserList = paginationInterceptor.getSqlParserList();
if (sqlParserList == null) {
sqlParserList = new ArrayList();
}
if (!Objects.isNull(this.env) && !this.env.contains("dev")) {
((List)sqlParserList).add(new BlockAttackSqlParser());
}
DataTenantSqlParser tenantSqlParser = new DataTenantSqlParser();
tenantSqlParser.setDataTenantHandler(preDataTenantHandler);
((List)sqlParserList).add(tenantSqlParser);
paginationInterceptor.setSqlParserList((List)sqlParserList);
return tenantSqlParser;
}
@Bean
@ConditionalOnMissingBean({DataTenantHolder.class})
public DataTenantHolder dataTenantHolder(DataTenantConfig dataTenantConfig) {
EntityDataTenantHolder entityDataTenantHolder = new EntityDataTenantHolder();
return entityDataTenantHolder;
}
@Bean
@ConditionalOnProperty(
name = {"boot.tenant.useTenantId"},
havingValue = "true"
)
@ConditionalOnMissingBean({FactorTenantSqlParser.class})
public FactorTenantSqlParser factorTenantSqlParser(PaginationInterceptor paginationInterceptor, PreTenantHandler preTenantHandler) {
List<ISqlParser> sqlParserList = paginationInterceptor.getSqlParserList();
if (sqlParserList == null) {
sqlParserList = new ArrayList();
}
if (!Objects.isNull(this.env) && !this.env.contains("dev")) {
((List)sqlParserList).add(new BlockAttackSqlParser());
}
FactorTenantSqlParser tenantSqlParser = new FactorTenantSqlParser();
tenantSqlParser.setDataTenantHandler(preTenantHandler);
((List)sqlParserList).add(tenantSqlParser);
paginationInterceptor.setSqlParserList((List)sqlParserList);
return tenantSqlParser;
}
@Bean
public PreDataTenantHandler preDataTenantHandler() {
return new PreDataTenantHandler();
}
@Bean
public PreTenantHandler preTenantHandler() {
return new PreTenantHandler();
}
@Bean
public DataTenantConfig dataTenantConfig() {
return new DataTenantConfig();
}