1.增加权限注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Classname Scope
* @Description TODO
* @Version 1.0.0
* @Date 2024/4/23 08:46
* @Created by lionel
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface LmScope {
boolean dataPermission() default true;
}
2.实现Mybatis-plus InnerInterceptor和hander
import net.sf.jsqlparser.expression.Expression;
/**
* @Classname ScopeLineHandler
* @Description TODO
* @Version 1.0.0
* @Date 2024/4/22 17:25
* @Created by lionel
*/
public interface ScopeLineHandler {
/**
* 获取租户 ID 值表达式,只支持单个 ID 值
* <p>
*
* @return 租户 ID 值表达式
*/
Expression getOrgId();
/**
* 获取租户字段名
* <p>
* 默认字段名叫: tenant_id
*
* @return 租户字段名
*/
default String getOrgIdColumn() {
return "org_id";
}
/**
* 根据表名判断是否忽略拼接多租户条件
* <p>
* 默认都要进行解析并拼接多租户条件
*
* @param tableName 表名
* @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件
*/
default boolean ignoreTable(String tableName) {
return false;
}
}
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;
import com.mascj.kernel.common.context.LmContextHolder;
import com.mascj.kernel.common.util.StringUtil;
import com.mascj.kernel.database.annotation.LmScope;
import lombok.*;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
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.*;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;
/**
* @Classname ScopeLineInnerInterceptor
* @Description TODO
* @Version 1.0.0
* @Date 2024/4/22 17:25
* @Created by lionel
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@SuppressWarnings({"rawtypes"})
public class ScopeLineInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {
private ScopeLineHandler scopeLineHandler;
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
//
if (!isAnnotation(ms.getId())) return;
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
mpBs.sql(parserSingle(mpBs.sql(), null));
}
@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
SqlCommandType sct = ms.getSqlCommandType();
if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
if (!isAnnotation(ms.getId())) return;
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(parserMulti(mpBs.sql(), null));
}
}
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
processSelectBody(select.getSelectBody());
List<WithItem> withItemsList = select.getWithItemsList();
if (!CollectionUtils.isEmpty(withItemsList)) {
withItemsList.forEach(this::processSelectBody);
}
}
// 判断是否有注解
@SneakyThrows(Exception.class)
public Boolean isAnnotation(String name) {
//获取mapper名称
String className = name.substring(0, name.lastIndexOf("."));
//获取方法名
String methodName = name.substring(name.lastIndexOf(".") + 1);
//获取当前mapper 的方法
Method[] methods = Class.forName(className).getMethods();
Method method = Arrays.stream(methods).filter(li -> li.getName().equals(methodName)).findAny().orElse(null);
if (Objects.isNull(method) || Objects.isNull(method.getAnnotation(LmScope.class))) {
return false;
}
// 判断是否有注解
LmScope annotation = method.getAnnotation(LmScope.class);
return annotation.dataPermission();
}
protected void processSelectBody(SelectBody selectBody) {
if (selectBody == null) {
return;
}
if (selectBody instanceof PlainSelect) {
processPlainSelect((PlainSelect) selectBody);
} else if (selectBody instanceof WithItem) {
WithItem withItem = (WithItem) selectBody;
processSelectBody(withItem.getSelectBody());
} else {
SetOperationList operationList = (SetOperationList) selectBody;
List<SelectBody> selectBodys = operationList.getSelects();
if (CollectionUtils.isNotEmpty(selectBodys)) {
selectBodys.forEach(this::processSelectBody);
}
}
}
@Override
protected void processInsert(Insert insert, int index, String sql, Object obj) {
if (scopeLineHandler.ignoreTable(insert.getTable().getName())) {
// 过滤退出执行
return;
}
List<Column> columns = insert.getColumns();
if (CollectionUtils.isEmpty(columns)) {
// 针对不给列名的insert 不处理
return;
}
String tenantIdColumn = scopeLineHandler.getOrgIdColumn();
if (columns.stream().map(Column::getColumnName).anyMatch(i -> i.equals(tenantIdColumn))) {
// 针对已给出租户列的insert 不处理
return;
}
columns.add(new Column(scopeLineHandler.getOrgIdColumn()));
// fixed gitee pulls/141 duplicate update
List<Expression> duplicateUpdateColumns = insert.getDuplicateUpdateExpressionList();
if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(new StringValue(scopeLineHandler.getOrgIdColumn()));
equalsTo.setRightExpression(scopeLineHandler.getOrgId());
duplicateUpdateColumns.add(equalsTo);
}
Select select = insert.getSelect();
if (select != null) {
this.processInsertSelect(select.getSelectBody());
} else if (insert.getItemsList() != null) {
// fixed github pull/295
ItemsList itemsList = insert.getItemsList();
if (itemsList instanceof MultiExpressionList) {
((MultiExpressionList) itemsList).getExprList().forEach(el -> el.getExpressions().add(scopeLineHandler.getOrgId()));
} else {
((ExpressionList) itemsList).getExpressions().add(scopeLineHandler.getOrgId());
}
} else {
throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId");
}
}
/**
* update 语句处理
*/
@Override
protected void processUpdate(Update update, int index, String sql, Object obj) {
final Table table = update.getTable();
if (scopeLineHandler.ignoreTable(table.getName())) {
// 过滤退出执行
return;
}
update.setWhere(this.andExpression(table, update.getWhere()));
}
/**
* delete 语句处理
*/
@Override
protected void processDelete(Delete delete, int index, String sql, Object obj) {
if (scopeLineHandler.ignoreTable(delete.getTable().getName())) {
// 过滤退出执行
return;
}
delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere()));
}
/**
* delete update 语句 where 处理
*/
protected BinaryExpression andExpression(Table table, Expression where) {
//获得where条件表达式
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(this.getAliasColumn(table));
equalsTo.setRightExpression(scopeLineHandler.getOrgId());
if (null != where) {
if (where instanceof OrExpression) {
return new AndExpression(equalsTo, new Parenthesis(where));
} else {
return new AndExpression(equalsTo, where);
}
}
return equalsTo;
}
/**
* 处理 insert into select
* <p>
* 进入这里表示需要 insert 的表启用了多租户,则 select 的表都启动了
*
* @param selectBody SelectBody
*/
protected void processInsertSelect(SelectBody selectBody) {
PlainSelect plainSelect = (PlainSelect) selectBody;
FromItem fromItem = plainSelect.getFromItem();
if (fromItem instanceof Table) {
// fixed gitee pulls/141 duplicate update
processPlainSelect(plainSelect);
appendSelectItem(plainSelect.getSelectItems());
} else if (fromItem instanceof SubSelect) {
SubSelect subSelect = (SubSelect) fromItem;
appendSelectItem(plainSelect.getSelectItems());
processInsertSelect(subSelect.getSelectBody());
}
}
/**
* 追加 SelectItem
*
* @param selectItems SelectItem
*/
protected void appendSelectItem(List<SelectItem> selectItems) {
if (CollectionUtils.isEmpty(selectItems)) return;
if (selectItems.size() == 1) {
SelectItem item = selectItems.get(0);
if (item instanceof AllColumns || item instanceof AllTableColumns) return;
}
selectItems.add(new SelectExpressionItem(new Column(scopeLineHandler.getOrgIdColumn())));
}
/**
* 处理 PlainSelect
*/
protected void processPlainSelect(PlainSelect plainSelect) {
FromItem fromItem = plainSelect.getFromItem();
Expression where = plainSelect.getWhere();
processWhereSubSelect(where);
if (fromItem instanceof Table) {
Table fromTable = (Table) fromItem;
if (!scopeLineHandler.ignoreTable(fromTable.getName())) {
//#1186 github
plainSelect.setWhere(builderExpression(where, fromTable));
}
} else {
processFromItem(fromItem);
}
//#3087 github
List<SelectItem> selectItems = plainSelect.getSelectItems();
if (CollectionUtils.isNotEmpty(selectItems)) {
selectItems.forEach(this::processSelectItem);
}
List<Join> joins = plainSelect.getJoins();
if (CollectionUtils.isNotEmpty(joins)) {
joins.forEach(j -> {
processJoin(j);
processFromItem(j.getRightItem());
});
}
}
/**
* 处理where条件内的子查询
* <p>
* 支持如下:
* 1. in
* 2. =
* 3. >
* 4. <
* 5. >=
* 6. <=
* 7. <>
* 8. EXISTS
* 9. NOT EXISTS
* <p>
* 前提条件:
* 1. 子查询必须放在小括号中
* 2. 子查询一般放在比较操作符的右边
*
* @param where where 条件
*/
protected void processWhereSubSelect(Expression where) {
if (where == null) {
return;
}
if (where instanceof FromItem) {
processFromItem((FromItem) where);
return;
}
if (where.toString().indexOf("SELECT") > 0) {
// 有子查询
if (where instanceof BinaryExpression) {
// 比较符号 , and , or , 等等
BinaryExpression expression = (BinaryExpression) where;
processWhereSubSelect(expression.getLeftExpression());
processWhereSubSelect(expression.getRightExpression());
} else if (where instanceof InExpression) {
// in
InExpression expression = (InExpression) where;
ItemsList itemsList = expression.getRightItemsList();
if (itemsList instanceof SubSelect) {
processSelectBody(((SubSelect) itemsList).getSelectBody());
}
} else if (where instanceof ExistsExpression) {
// exists
ExistsExpression expression = (ExistsExpression) where;
processWhereSubSelect(expression.getRightExpression());
} else if (where instanceof NotExpression) {
// not exists
NotExpression expression = (NotExpression) where;
processWhereSubSelect(expression.getExpression());
} else if (where instanceof Parenthesis) {
Parenthesis expression = (Parenthesis) where;
processWhereSubSelect(expression.getExpression());
}
}
}
protected void processSelectItem(SelectItem selectItem) {
if (selectItem instanceof SelectExpressionItem) {
SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
if (selectExpressionItem.getExpression() instanceof SubSelect) {
processSelectBody(((SubSelect) selectExpressionItem.getExpression()).getSelectBody());
} else if (selectExpressionItem.getExpression() instanceof Function) {
processFunction((Function) selectExpressionItem.getExpression());
}
}
}
/**
* 处理函数
* <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>
* <p> fixed gitee pulls/141</p>
*
* @param function
*/
protected void processFunction(Function function) {
ExpressionList parameters = function.getParameters();
if (parameters != null) {
parameters.getExpressions().forEach(expression -> {
if (expression instanceof SubSelect) {
processSelectBody(((SubSelect) expression).getSelectBody());
} else if (expression instanceof Function) {
processFunction((Function) expression);
}
});
}
}
/**
* 处理子查询等
*/
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) {
processFromItem(subJoin.getLeft());
}
} else if (fromItem instanceof SubSelect) {
SubSelect subSelect = (SubSelect) fromItem;
if (subSelect.getSelectBody() != null) {
processSelectBody(subSelect.getSelectBody());
}
} else if (fromItem instanceof ValuesList) {
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) {
processSelectBody(subSelect.getSelectBody());
}
}
}
}
/**
* 处理联接语句
*/
protected void processJoin(Join join) {
if (join.getRightItem() instanceof Table) {
Table fromTable = (Table) join.getRightItem();
if (scopeLineHandler.ignoreTable(fromTable.getName())) {
// 过滤退出执行
return;
}
join.setOnExpression(builderExpression(join.getOnExpression(), fromTable));
}
}
/**
* 处理条件
*/
protected Expression builderExpression(Expression currentExpression, Table table) {
if (LmContextHolder.getScope().equals(4)) {
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(this.getAliasColumn(table));
equalsTo.setRightExpression(scopeLineHandler.getOrgId());
if (currentExpression == null) {
return equalsTo;
}
if (currentExpression instanceof OrExpression) {
return new AndExpression(new Parenthesis(currentExpression), equalsTo);
} else {
return new AndExpression(currentExpression, equalsTo);
}
} else {
// 把集合转变为JSQLParser需要的元素列表
String orgIds = LmContextHolder.getOrgIds();
if (StringUtil.isBlank(orgIds)) {
throw new RuntimeException("org_ids is empty");
}
List<String> ids = Arrays.asList(orgIds.split(","));
ItemsList itemsList = new ExpressionList(ids.stream().map(LongValue::new).collect(Collectors.toList()));
InExpression inExpression = new InExpression(this.getAliasColumn(table), itemsList);
if (currentExpression instanceof OrExpression) {
return new AndExpression(new Parenthesis(currentExpression), inExpression);
} else {
return new AndExpression(currentExpression, inExpression);
}
}
}
/**
* 租户字段别名设置
* <p>tenantId 或 tableAlias.tenantId</p>
*
* @param table 表对象
* @return 字段
*/
protected Column getAliasColumn(Table table) {
StringBuilder column = new StringBuilder();
if (table.getAlias() != null) {
column.append(table.getAlias().getName()).append(StringPool.DOT);
}
if (LmContextHolder.getScope().equals(4)) {
// column.append(scopeLineHandler.getOrgIdColumn());
column.append("create_by");
} else {
column.append(scopeLineHandler.getOrgIdColumn());
}
return new Column(column.toString());
}
@Override
public void setProperties(Properties properties) {
PropertyMapper.newInstance(properties)
.whenNotBlack("scopeLineHandler", ClassUtils::newInstance, this::setScopeLineHandler);
}
}
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;
import com.mascj.kernel.common.context.LmContextHolder;
import com.mascj.kernel.common.util.StringUtil;
import com.mascj.kernel.database.annotation.LmScope;
import lombok.*;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
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.*;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;
/**
* @Classname ScopeLineInnerInterceptor
* @Description TODO
* @Version 1.0.0
* @Date 2024/4/22 17:25
* @Created by lionel
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@SuppressWarnings({"rawtypes"})
public class ScopeLineInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {
private ScopeLineHandler scopeLineHandler;
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
//
if (!isAnnotation(ms.getId())) return;
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
mpBs.sql(parserSingle(mpBs.sql(), null));
}
@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
SqlCommandType sct = ms.getSqlCommandType();
if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
if (!isAnnotation(ms.getId())) return;
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(parserMulti(mpBs.sql(), null));
}
}
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
processSelectBody(select.getSelectBody());
List<WithItem> withItemsList = select.getWithItemsList();
if (!CollectionUtils.isEmpty(withItemsList)) {
withItemsList.forEach(this::processSelectBody);
}
}
// 判断是否有注解
@SneakyThrows(Exception.class)
public Boolean isAnnotation(String name) {
//获取mapper名称
String className = name.substring(0, name.lastIndexOf("."));
//获取方法名
String methodName = name.substring(name.lastIndexOf(".") + 1);
//获取当前mapper 的方法
Method[] methods = Class.forName(className).getMethods();
Method method = Arrays.stream(methods).filter(li -> li.getName().equals(methodName)).findAny().orElse(null);
if (Objects.isNull(method) || Objects.isNull(method.getAnnotation(LmScope.class))) {
return false;
}
// 判断是否有注解
LmScope annotation = method.getAnnotation(LmScope.class);
return annotation.dataPermission();
}
protected void processSelectBody(SelectBody selectBody) {
if (selectBody == null) {
return;
}
if (selectBody instanceof PlainSelect) {
processPlainSelect((PlainSelect) selectBody);
} else if (selectBody instanceof WithItem) {
WithItem withItem = (WithItem) selectBody;
processSelectBody(withItem.getSelectBody());
} else {
SetOperationList operationList = (SetOperationList) selectBody;
List<SelectBody> selectBodys = operationList.getSelects();
if (CollectionUtils.isNotEmpty(selectBodys)) {
selectBodys.forEach(this::processSelectBody);
}
}
}
@Override
protected void processInsert(Insert insert, int index, String sql, Object obj) {
if (scopeLineHandler.ignoreTable(insert.getTable().getName())) {
// 过滤退出执行
return;
}
List<Column> columns = insert.getColumns();
if (CollectionUtils.isEmpty(columns)) {
// 针对不给列名的insert 不处理
return;
}
String tenantIdColumn = scopeLineHandler.getOrgIdColumn();
if (columns.stream().map(Column::getColumnName).anyMatch(i -> i.equals(tenantIdColumn))) {
// 针对已给出租户列的insert 不处理
return;
}
columns.add(new Column(scopeLineHandler.getOrgIdColumn()));
// fixed gitee pulls/141 duplicate update
List<Expression> duplicateUpdateColumns = insert.getDuplicateUpdateExpressionList();
if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(new StringValue(scopeLineHandler.getOrgIdColumn()));
equalsTo.setRightExpression(scopeLineHandler.getOrgId());
duplicateUpdateColumns.add(equalsTo);
}
Select select = insert.getSelect();
if (select != null) {
this.processInsertSelect(select.getSelectBody());
} else if (insert.getItemsList() != null) {
// fixed github pull/295
ItemsList itemsList = insert.getItemsList();
if (itemsList instanceof MultiExpressionList) {
((MultiExpressionList) itemsList).getExprList().forEach(el -> el.getExpressions().add(scopeLineHandler.getOrgId()));
} else {
((ExpressionList) itemsList).getExpressions().add(scopeLineHandler.getOrgId());
}
} else {
throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId");
}
}
/**
* update 语句处理
*/
@Override
protected void processUpdate(Update update, int index, String sql, Object obj) {
final Table table = update.getTable();
if (scopeLineHandler.ignoreTable(table.getName())) {
// 过滤退出执行
return;
}
update.setWhere(this.andExpression(table, update.getWhere()));
}
/**
* delete 语句处理
*/
@Override
protected void processDelete(Delete delete, int index, String sql, Object obj) {
if (scopeLineHandler.ignoreTable(delete.getTable().getName())) {
// 过滤退出执行
return;
}
delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere()));
}
/**
* delete update 语句 where 处理
*/
protected BinaryExpression andExpression(Table table, Expression where) {
//获得where条件表达式
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(this.getAliasColumn(table));
equalsTo.setRightExpression(scopeLineHandler.getOrgId());
if (null != where) {
if (where instanceof OrExpression) {
return new AndExpression(equalsTo, new Parenthesis(where));
} else {
return new AndExpression(equalsTo, where);
}
}
return equalsTo;
}
/**
* 处理 insert into select
* <p>
* 进入这里表示需要 insert 的表启用了多租户,则 select 的表都启动了
*
* @param selectBody SelectBody
*/
protected void processInsertSelect(SelectBody selectBody) {
PlainSelect plainSelect = (PlainSelect) selectBody;
FromItem fromItem = plainSelect.getFromItem();
if (fromItem instanceof Table) {
// fixed gitee pulls/141 duplicate update
processPlainSelect(plainSelect);
appendSelectItem(plainSelect.getSelectItems());
} else if (fromItem instanceof SubSelect) {
SubSelect subSelect = (SubSelect) fromItem;
appendSelectItem(plainSelect.getSelectItems());
processInsertSelect(subSelect.getSelectBody());
}
}
/**
* 追加 SelectItem
*
* @param selectItems SelectItem
*/
protected void appendSelectItem(List<SelectItem> selectItems) {
if (CollectionUtils.isEmpty(selectItems)) return;
if (selectItems.size() == 1) {
SelectItem item = selectItems.get(0);
if (item instanceof AllColumns || item instanceof AllTableColumns) return;
}
selectItems.add(new SelectExpressionItem(new Column(scopeLineHandler.getOrgIdColumn())));
}
/**
* 处理 PlainSelect
*/
protected void processPlainSelect(PlainSelect plainSelect) {
FromItem fromItem = plainSelect.getFromItem();
Expression where = plainSelect.getWhere();
processWhereSubSelect(where);
if (fromItem instanceof Table) {
Table fromTable = (Table) fromItem;
if (!scopeLineHandler.ignoreTable(fromTable.getName())) {
//#1186 github
plainSelect.setWhere(builderExpression(where, fromTable));
}
} else {
processFromItem(fromItem);
}
//#3087 github
List<SelectItem> selectItems = plainSelect.getSelectItems();
if (CollectionUtils.isNotEmpty(selectItems)) {
selectItems.forEach(this::processSelectItem);
}
List<Join> joins = plainSelect.getJoins();
if (CollectionUtils.isNotEmpty(joins)) {
joins.forEach(j -> {
processJoin(j);
processFromItem(j.getRightItem());
});
}
}
/**
* 处理where条件内的子查询
* <p>
* 支持如下:
* 1. in
* 2. =
* 3. >
* 4. <
* 5. >=
* 6. <=
* 7. <>
* 8. EXISTS
* 9. NOT EXISTS
* <p>
* 前提条件:
* 1. 子查询必须放在小括号中
* 2. 子查询一般放在比较操作符的右边
*
* @param where where 条件
*/
protected void processWhereSubSelect(Expression where) {
if (where == null) {
return;
}
if (where instanceof FromItem) {
processFromItem((FromItem) where);
return;
}
if (where.toString().indexOf("SELECT") > 0) {
// 有子查询
if (where instanceof BinaryExpression) {
// 比较符号 , and , or , 等等
BinaryExpression expression = (BinaryExpression) where;
processWhereSubSelect(expression.getLeftExpression());
processWhereSubSelect(expression.getRightExpression());
} else if (where instanceof InExpression) {
// in
InExpression expression = (InExpression) where;
ItemsList itemsList = expression.getRightItemsList();
if (itemsList instanceof SubSelect) {
processSelectBody(((SubSelect) itemsList).getSelectBody());
}
} else if (where instanceof ExistsExpression) {
// exists
ExistsExpression expression = (ExistsExpression) where;
processWhereSubSelect(expression.getRightExpression());
} else if (where instanceof NotExpression) {
// not exists
NotExpression expression = (NotExpression) where;
processWhereSubSelect(expression.getExpression());
} else if (where instanceof Parenthesis) {
Parenthesis expression = (Parenthesis) where;
processWhereSubSelect(expression.getExpression());
}
}
}
protected void processSelectItem(SelectItem selectItem) {
if (selectItem instanceof SelectExpressionItem) {
SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
if (selectExpressionItem.getExpression() instanceof SubSelect) {
processSelectBody(((SubSelect) selectExpressionItem.getExpression()).getSelectBody());
} else if (selectExpressionItem.getExpression() instanceof Function) {
processFunction((Function) selectExpressionItem.getExpression());
}
}
}
/**
* 处理函数
* <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>
* <p> fixed gitee pulls/141</p>
*
* @param function
*/
protected void processFunction(Function function) {
ExpressionList parameters = function.getParameters();
if (parameters != null) {
parameters.getExpressions().forEach(expression -> {
if (expression instanceof SubSelect) {
processSelectBody(((SubSelect) expression).getSelectBody());
} else if (expression instanceof Function) {
processFunction((Function) expression);
}
});
}
}
/**
* 处理子查询等
*/
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) {
processFromItem(subJoin.getLeft());
}
} else if (fromItem instanceof SubSelect) {
SubSelect subSelect = (SubSelect) fromItem;
if (subSelect.getSelectBody() != null) {
processSelectBody(subSelect.getSelectBody());
}
} else if (fromItem instanceof ValuesList) {
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) {
processSelectBody(subSelect.getSelectBody());
}
}
}
}
/**
* 处理联接语句
*/
protected void processJoin(Join join) {
if (join.getRightItem() instanceof Table) {
Table fromTable = (Table) join.getRightItem();
if (scopeLineHandler.ignoreTable(fromTable.getName())) {
// 过滤退出执行
return;
}
join.setOnExpression(builderExpression(join.getOnExpression(), fromTable));
}
}
/**
* 处理条件
*/
protected Expression builderExpression(Expression currentExpression, Table table) {
if (LmContextHolder.getScope().equals(4)) {
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(this.getAliasColumn(table));
equalsTo.setRightExpression(scopeLineHandler.getOrgId());
if (currentExpression == null) {
return equalsTo;
}
if (currentExpression instanceof OrExpression) {
return new AndExpression(new Parenthesis(currentExpression), equalsTo);
} else {
return new AndExpression(currentExpression, equalsTo);
}
} else {
// 把集合转变为JSQLParser需要的元素列表
String orgIds = LmContextHolder.getOrgIds();
if (StringUtil.isBlank(orgIds)) {
throw new RuntimeException("org_ids is empty");
}
List<String> ids = Arrays.asList(orgIds.split(","));
ItemsList itemsList = new ExpressionList(ids.stream().map(LongValue::new).collect(Collectors.toList()));
InExpression inExpression = new InExpression(this.getAliasColumn(table), itemsList);
if (currentExpression instanceof OrExpression) {
return new AndExpression(new Parenthesis(currentExpression), inExpression);
} else {
return new AndExpression(currentExpression, inExpression);
}
}
}
/**
* 租户字段别名设置
* <p>tenantId 或 tableAlias.tenantId</p>
*
* @param table 表对象
* @return 字段
*/
protected Column getAliasColumn(Table table) {
StringBuilder column = new StringBuilder();
if (table.getAlias() != null) {
column.append(table.getAlias().getName()).append(StringPool.DOT);
}
if (LmContextHolder.getScope().equals(4)) {
// column.append(scopeLineHandler.getOrgIdColumn());
column.append("create_by");
} else {
column.append(scopeLineHandler.getOrgIdColumn());
}
return new Column(column.toString());
}
@Override
public void setProperties(Properties properties) {
PropertyMapper.newInstance(properties)
.whenNotBlack("scopeLineHandler", ClassUtils::newInstance, this::setScopeLineHandler);
}
}
import com.mascj.kernel.common.context.LmContextHolder;
import com.mascj.kernel.database.config.MybatisPlusConfiguration;
import com.mascj.kernel.database.props.ScopeProperties;
import lombok.AllArgsConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.StringValue;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 多租户配置中心
*
* @author Lance.wu
*/
@Configuration
@AllArgsConstructor
@AutoConfigureBefore(MybatisPlusConfiguration.class)
@EnableConfigurationProperties(ScopeProperties.class)
public class ScopeConfiguration {
private final ScopeProperties scopeProperties;
/**
* 新多租户插件配置,一缓和二缓遵循mybatis的规则,
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存万一出现问题
*
* @return TenantLineInnerInterceptor
*/
@Bean
public ScopeLineInnerInterceptor scopeLineInnerInterceptor() {
return new ScopeLineInnerInterceptor(new ScopeLineHandler() {
@Override
public Expression getOrgId() {
String tenant = LmContextHolder.getTenantId();
if (tenant != null) {
return new StringValue(LmContextHolder.getTenantId());
}
return new NullValue();
}
@Override
public String getOrgIdColumn() {
return scopeProperties.getColumn();
}
@Override
public boolean ignoreTable(String tableName) {
return scopeProperties.getIgnoreTables().stream().anyMatch(
(t) -> t.equalsIgnoreCase(tableName)
);
}
// /**
// * 获取租户ID
// * @return Expression
// */
// @Override
// public Expression ge() {
// String tenant = LmContextHolder.getTenantId();
// if (tenant != null) {
// return new StringValue(LmContextHolder.getTenantId());
// }
// return new NullValue();
// }
//
// /**
// * 获取多租户的字段名
// * @return String
// */
// @Override
// public String getTenantIdColumn() {
// return tenantProperties.getColumn();
// }
//
// /**
// * 过滤不需要根据租户隔离的表
// * 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
// * @param tableName 表名
// */
// @Override
// public boolean ignoreTable(String tableName) {
// return tenantProperties.getIgnoreTables().stream().anyMatch(
// (t) -> t.equalsIgnoreCase(tableName)
// );
// }
});
}
}
3.增加scopeProperties
package com.mascj.kernel.database.props;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 租户属性
*
* @author Lance.wu
*/
@Getter
@Setter
@RefreshScope
@ConfigurationProperties(prefix = "liangma.scope")
public class ScopeProperties {
/**
* 是否开启租户模式
*/
private Boolean enable = true;
/**
* 需要排除的多租户的表
*/
private List<String> ignoreTables = Arrays.asList(
"lmc_test",
"lmc_sys_menu",
//"lmc_sys_role",
"lmc_sys_role_data_scope",
"lmc_sys_role_menu",
//"lmc_sys_user",
"lmc_sys_user_data_scope",
"lmc_sys_user_role",
//"lmc_sys_org",
//"lmc_sys_pos",
"lmc_sys_emp",
"lmc_sys_emp_ext_org_pos",
"lmc_sys_emp_pos",
"lmc_sys_app",
"lmc_sys_dict",
"lmc_sys_client",
"lmc_sys_data_source",
"lmc_sys_code",
"lmc_sys_code_config",
"lmc_sys_set",
"lmc_sys_material_group",
"lmc_sys_material",
"lmc_sys_api",
"lmc_sys_blacklist",
"lmc_sys_rate_limit",
"lmc_sys_route",
"lmc_sys_tenant",
"lmc_sys_tenant_domain",
//"lmc_sys_notice",
"lmc_sys_notice_user",
"lmc_sys_sms",
//"lmc_sys_log",
//"lmc_sys_login_log",
"lmc_app_user",
"lmc_i18n_data",
"lmc_sys_user_open",
"lmc_sys_micro_user"
);
/**
* 多租户字段名称
*/
private String column = "org_id";
/**
* 排除不进行租户隔离的sql
* 样例全路径:com.mascj.system.mapper.UserMapper.findList
*/
private List<String> ignoreSqls = new ArrayList<>();
}
4.修改Mybatis-plus Bean
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
boolean enableTenant = tenantProperties.getEnable();
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
if (enableTenant) {
interceptor.addInnerInterceptor(tenantLineInnerInterceptor);
}
//分页插件: PaginationInnerInterceptor
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setMaxLimit(MAX_LIMIT);
//防止全表更新与删除插件: BlockAttackInnerInterceptor
BlockAttackInnerInterceptor blockAttackInnerInterceptor = new BlockAttackInnerInterceptor();
interceptor.addInnerInterceptor(paginationInnerInterceptor);
interceptor.addInnerInterceptor(blockAttackInnerInterceptor);
// mybatis-plus 乐观锁拦截器
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}