xiejun
2024-11-01 80b6cbfc9c861469146318d0b3dd5f8b8b525b8a
Source/BladeX-Tool/blade-starter-tenant/src/main/java/org/springblade/core/tenant/BladeTenantInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,419 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill åº„骞 (smallchill@163.com)
 */
package org.springblade.core.tenant;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
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.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
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.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.StringPool;
import java.util.*;
import java.util.stream.Collectors;
/**
 * ç§Ÿæˆ·æ‹¦æˆªå™¨
 *
 * @author Chill
 */
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class BladeTenantInterceptor extends TenantLineInnerInterceptor {
   /**
    * ç§Ÿæˆ·å¤„理器
    */
   private TenantLineHandler tenantLineHandler;
   /**
    * ç§Ÿæˆ·é…ç½®æ–‡ä»¶
    */
   private BladeTenantProperties tenantProperties;
   /**
    * è¶…管需要启用租户过滤的表
    */
   private List<String> adminTenantTables = Arrays.asList("blade_top_menu", "blade_dict_biz");
   @Override
   public void setTenantLineHandler(TenantLineHandler tenantLineHandler) {
      super.setTenantLineHandler(tenantLineHandler);
      this.tenantLineHandler = tenantLineHandler;
   }
   @Override
   protected void processInsert(Insert insert, int index, String sql, Object obj) {
      // æœªå¯ç”¨ç§Ÿæˆ·å¢žå¼ºï¼Œåˆ™ä½¿ç”¨åŽŸç‰ˆé€»è¾‘
      if (!tenantProperties.getEnhance()) {
         super.processInsert(insert, index, sql, obj);
         return;
      }
      if (tenantLineHandler.ignoreTable(insert.getTable().getName())) {
         // è¿‡æ»¤é€€å‡ºæ‰§è¡Œ
         return;
      }
      List<Column> columns = insert.getColumns();
      if (CollectionUtils.isEmpty(columns)) {
         // é’ˆå¯¹ä¸ç»™åˆ—名的insert ä¸å¤„理
         return;
      }
      String tenantIdColumn = tenantLineHandler.getTenantIdColumn();
      if (columns.stream().map(Column::getColumnName).anyMatch(i -> i.equals(tenantIdColumn))) {
         // é’ˆå¯¹å·²ç»™å‡ºç§Ÿæˆ·åˆ—çš„insert ä¸å¤„理
         return;
      }
      columns.add(new Column(tenantIdColumn));
      // fixed gitee pulls/141 duplicate update
      List<Expression> duplicateUpdateColumns = insert.getDuplicateUpdateExpressionList();
      if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {
         EqualsTo equalsTo = new EqualsTo();
         equalsTo.setLeftExpression(new StringValue(tenantIdColumn));
         equalsTo.setRightExpression(tenantLineHandler.getTenantId());
         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).getExpressionLists().forEach(el -> el.getExpressions().add(tenantLineHandler.getTenantId()));
         } else {
            ((ExpressionList) itemsList).getExpressions().add(tenantLineHandler.getTenantId());
         }
      } else {
         throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId");
      }
   }
   /**
    * å¤„理 PlainSelect
    */
   @Override
   protected void processPlainSelect(PlainSelect plainSelect) {
      //#3087 github
      List<SelectItem> selectItems = plainSelect.getSelectItems();
      if (CollectionUtils.isNotEmpty(selectItems)) {
         selectItems.forEach(this::processSelectItem);
      }
      // å¤„理 where ä¸­çš„子查询
      Expression where = plainSelect.getWhere();
      processWhereSubSelect(where);
      // å¤„理 fromItem
      FromItem fromItem = plainSelect.getFromItem();
      List<Table> list = processFromItem(fromItem);
      List<Table> mainTables = new ArrayList<>(list);
      // å¤„理 join
      List<Join> joins = plainSelect.getJoins();
      if (CollectionUtils.isNotEmpty(joins)) {
         mainTables = processJoins(mainTables, joins);
      }
      // å½“有 mainTable æ—¶ï¼Œè¿›è¡Œ where æ¡ä»¶è¿½åŠ 
      if (CollectionUtils.isNotEmpty(mainTables) && !doTenantFilters(mainTables)) {
         plainSelect.setWhere(builderExpression(where, mainTables));
      }
   }
   /**
    * update è¯­å¥å¤„理
    */
   @Override
   protected void processUpdate(Update update, int index, String sql, Object obj) {
      final Table table = update.getTable();
      if (tenantLineHandler.ignoreTable(table.getName())) {
         // è¿‡æ»¤é€€å‡ºæ‰§è¡Œ
         return;
      }
      if (doTenantFilter(table.getName())) {
         // è¿‡æ»¤é€€å‡ºæ‰§è¡Œ
         return;
      }
      update.setWhere(this.andExpression(table, update.getWhere()));
   }
   /**
    * delete è¯­å¥å¤„理
    */
   @Override
   protected void processDelete(Delete delete, int index, String sql, Object obj) {
      final Table table = delete.getTable();
      if (tenantLineHandler.ignoreTable(table.getName())) {
         // è¿‡æ»¤é€€å‡ºæ‰§è¡Œ
         return;
      }
      if (doTenantFilter(table.getName())) {
         // è¿‡æ»¤é€€å‡ºæ‰§è¡Œ
         return;
      }
      delete.setWhere(this.andExpression(table, delete.getWhere()));
   }
   /**
    * delete update è¯­å¥ where å¤„理
    */
   @Override
   protected BinaryExpression andExpression(Table table, Expression where) {
      //获得条件表达式
      EqualsTo equalsTo = new EqualsTo();
      Expression leftExpression = this.getAliasColumn(table);
      Expression rightExpression = tenantLineHandler.getTenantId();
      // è‹¥æ˜¯è¶…管则不进行过滤
      if (doTenantFilter(table.getName())) {
         leftExpression = rightExpression = new StringValue(StringPool.ONE);
      }
      equalsTo.setLeftExpression(leftExpression);
      equalsTo.setRightExpression(rightExpression);
      if (null != where) {
         if (where instanceof OrExpression) {
            return new AndExpression(equalsTo, new Parenthesis(where));
         } else {
            return new AndExpression(equalsTo, where);
         }
      }
      return equalsTo;
   }
   /**
    * å¢žå¼ºæ’件使超级管理员可以看到所有租户数据
    */
   @Override
   protected Expression builderExpression(Expression currentExpression, List<Table> tables) {
      // æ²¡æœ‰è¡¨éœ€è¦å¤„理直接返回
      if (CollectionUtils.isEmpty(tables)) {
         return currentExpression;
      }
      // ç§Ÿæˆ·
      Expression tenantId = tenantLineHandler.getTenantId();
      // æž„造每张表的条件
      List<EqualsTo> equalsTos = tables.stream()
         // ç§Ÿæˆ·å¿½ç•¥è¡¨
         .filter(x -> !tenantLineHandler.ignoreTable(x.getName()))
         // è¶…管忽略表
         .filter(x -> !doTenantFilter(x.getName()))
         .map(item -> new EqualsTo(getAliasColumn(item), tenantId))
         .collect(Collectors.toList());
      if (CollectionUtils.isEmpty(equalsTos)) {
         return currentExpression;
      }
      // æ³¨å…¥çš„表达式
      Expression injectExpression = equalsTos.get(0);
      // å¦‚果有多表,则用 and è¿žæŽ¥
      if (equalsTos.size() > 1) {
         for (int i = 1; i < equalsTos.size(); i++) {
            injectExpression = new AndExpression(injectExpression, equalsTos.get(i));
         }
      }
      if (currentExpression == null) {
         return injectExpression;
      }
      if (currentExpression instanceof OrExpression) {
         return new AndExpression(new Parenthesis(currentExpression), injectExpression);
      } else {
         return new AndExpression(currentExpression, injectExpression);
      }
   }
   private List<Table> processFromItem(FromItem fromItem) {
      // å¤„理括号括起来的表达式
      while (fromItem instanceof ParenthesisFromItem) {
         fromItem = ((ParenthesisFromItem) fromItem).getFromItem();
      }
      List<Table> mainTables = new ArrayList<>();
      // æ—  join æ—¶çš„处理逻辑
      if (fromItem instanceof Table) {
         Table fromTable = (Table) fromItem;
         mainTables.add(fromTable);
      } else if (fromItem instanceof SubJoin) {
         // SubJoin ç±»åž‹åˆ™è¿˜éœ€è¦æ·»åŠ ä¸Š where æ¡ä»¶
         List<Table> tables = processSubJoin((SubJoin) fromItem);
         mainTables.addAll(tables);
      } else {
         // å¤„理下 fromItem
         processOtherFromItem(fromItem);
      }
      return mainTables;
   }
   /**
    * å¤„理 sub join
    *
    * @param subJoin subJoin
    * @return Table subJoin ä¸­çš„主表
    */
   private List<Table> processSubJoin(SubJoin subJoin) {
      List<Table> mainTables = new ArrayList<>();
      if (subJoin.getJoinList() != null) {
         List<Table> list = processFromItem(subJoin.getLeft());
         mainTables.addAll(list);
         mainTables = processJoins(mainTables, subJoin.getJoinList());
      }
      return mainTables;
   }
   /**
    * å¤„理 joins
    *
    * @param mainTables å¯ä»¥ä¸º null
    * @param joins      join é›†åˆ
    * @return List<Table> å³è¿žæŽ¥æŸ¥è¯¢çš„ Table åˆ—表
    */
   private List<Table> processJoins(List<Table> mainTables, List<Join> joins) {
      // join è¡¨è¾¾å¼ä¸­æœ€ç»ˆçš„主表
      Table mainTable = null;
      // å½“前 join çš„左表
      Table leftTable = null;
      if (mainTables == null) {
         mainTables = new ArrayList<>();
      } else if (mainTables.size() == 1) {
         mainTable = mainTables.get(0);
         leftTable = mainTable;
      }
      //对于 on è¡¨è¾¾å¼å†™åœ¨æœ€åŽçš„ join,需要记录下前面多个 on çš„表名
      Deque<List<Table>> onTableDeque = new LinkedList<>();
      for (Join join : joins) {
         // å¤„理 on è¡¨è¾¾å¼
         FromItem joinItem = join.getRightItem();
         // èŽ·å–å½“å‰ join çš„表,subJoint å¯ä»¥çœ‹ä½œæ˜¯ä¸€å¼ è¡¨
         List<Table> joinTables = null;
         if (joinItem instanceof Table) {
            joinTables = new ArrayList<>();
            joinTables.add((Table) joinItem);
         } else if (joinItem instanceof SubJoin) {
            joinTables = processSubJoin((SubJoin) joinItem);
         }
         if (joinTables != null) {
            // å¦‚果是隐式内连接
            if (join.isSimple()) {
               mainTables.addAll(joinTables);
               continue;
            }
            // å½“前表是否忽略
            Table joinTable = joinTables.get(0);
            List<Table> onTables = null;
            // å¦‚果不要忽略,且是右连接,则记录下当前表
            if (join.isRight()) {
               mainTable = joinTable;
               if (leftTable != null) {
                  onTables = Collections.singletonList(leftTable);
               }
            } else if (join.isLeft()) {
               onTables = Collections.singletonList(joinTable);
            } else if (join.isInner()) {
               if (mainTable == null) {
                  onTables = Collections.singletonList(joinTable);
               } else {
                  onTables = Arrays.asList(mainTable, joinTable);
               }
               mainTable = null;
            }
            mainTables = new ArrayList<>();
            if (mainTable != null) {
               mainTables.add(mainTable);
            }
            // èŽ·å– join å°¾ç¼€çš„ on è¡¨è¾¾å¼åˆ—表
            Collection<Expression> originOnExpressions = join.getOnExpressions();
            // æ­£å¸¸ join on è¡¨è¾¾å¼åªæœ‰ä¸€ä¸ªï¼Œç«‹åˆ»å¤„理
            if (originOnExpressions.size() == 1 && onTables != null) {
               List<Expression> onExpressions = new LinkedList<>();
               onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables));
               join.setOnExpressions(onExpressions);
               leftTable = joinTable;
               continue;
            }
            // è¡¨ååŽ‹æ ˆï¼Œå¿½ç•¥çš„è¡¨åŽ‹å…¥ null,以便后续不处理
            onTableDeque.push(onTables);
            // å°¾ç¼€å¤šä¸ª on è¡¨è¾¾å¼çš„æ—¶å€™ç»Ÿä¸€å¤„理
            if (originOnExpressions.size() > 1) {
               Collection<Expression> onExpressions = new LinkedList<>();
               for (Expression originOnExpression : originOnExpressions) {
                  List<Table> currentTableList = onTableDeque.poll();
                  if (CollectionUtils.isEmpty(currentTableList)) {
                     onExpressions.add(originOnExpression);
                  } else {
                     onExpressions.add(builderExpression(originOnExpression, currentTableList));
                  }
               }
               join.setOnExpressions(onExpressions);
            }
            leftTable = joinTable;
         } else {
            processOtherFromItem(joinItem);
            leftTable = null;
         }
      }
      return mainTables;
   }
   /**
    * åˆ¤æ–­å½“前操作是否需要进行过滤
    *
    * @param tableName è¡¨å
    */
   public boolean doTenantFilter(String tableName) {
      return AuthUtil.isAdministrator() && !adminTenantTables.contains(tableName);
   }
   /**
    * åˆ¤æ–­å½“前操作是否需要进行过滤
    *
    * @param tables è¡¨å
    */
   public boolean doTenantFilters(List<Table> tables) {
      List<String> tableNames = tables.stream().map(Table::getName).collect(Collectors.toList());
      return AuthUtil.isAdministrator() && !CollectionUtil.containsAny(adminTenantTables, tableNames);
   }
}