content: package com.seezoon.dao.framework.authority;
  
  import java.util.HashMap;
  import java.util.Objects;
  import java.util.Properties;
  
  import org.apache.ibatis.cache.CacheKey;
  import org.apache.ibatis.executor.Executor;
  import org.apache.ibatis.mapping.BoundSql;
  import org.apache.ibatis.mapping.MappedStatement;
  import org.apache.ibatis.mapping.SqlCommandType;
  import org.apache.ibatis.plugin.Interceptor;
  import org.apache.ibatis.plugin.Intercepts;
  import org.apache.ibatis.plugin.Invocation;
  import org.apache.ibatis.plugin.Signature;
  import org.apache.ibatis.reflection.MetaObject;
  import org.apache.ibatis.reflection.SystemMetaObject;
  import org.apache.ibatis.session.ResultHandler;
  import org.apache.ibatis.session.RowBounds;
  import org.apache.ibatis.type.SimpleTypeRegistry;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.github.pagehelper.PageHelper;
  import com.seezoon.dao.framework.entity.PageCondition;
  
  /**
   * @author hdf
   */
  @Intercepts({
      @Signature(type = Executor.class, method = "query",
          args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
      @Signature(type = Executor.class, method = "query",
          args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class,
              BoundSql.class}),
      @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
  public class DataAuthorityInterceptor implements Interceptor {
  
      protected final Logger logger = LoggerFactory.getLogger(DataAuthorityInterceptor.class);
  
      @Override
      public Object intercept(Invocation invocation) throws Throwable {
          DataAuthority dataAuthority = DataAuthorityLoader.getInstance();
          String dsf = null;
          if (Objects.isNull(dataAuthority)) {
              logger.info("DataAuthority Spi not found, disable data authority");
          } else {
              dsf = dataAuthority.getDsf();
          }
          Object[] args = invocation.getArgs();
          MappedStatement ms = (MappedStatement)args[0];
          SqlCommandType sqlCommandType = ms.getSqlCommandType();
          // skip insert
          if (sqlCommandType.equals(SqlCommandType.INSERT)) {
              return invocation.proceed();
          }
          Object parameter = (Object)args[1];
  
          // 成本最低的方式 这里主要是和PageHelper 拦截器针对参数处理不一样，PageHelper 对map类型（WrapContextMap） 直接用了putAll，
          // 如果要兼容需要把parameter 都放到map中，对简单类型字段
          if (sqlCommandType.equals(SqlCommandType.SELECT) && null != PageHelper.getLocalPage() && null != parameter
              && parameter instanceof PageCondition) {
              ((PageCondition)parameter).setDsf(dsf);
          } else {
              WrapContextMap wrapContextMap = new WrapContextMap(parameter);
              // 自定义附件参数，多租户也可以自定义参数
              wrapContextMap.put("dsf", dsf);
              args[1] = wrapContextMap;
          }
  
          return invocation.proceed();
      }
  
      @Override
      public void setProperties(Properties properties) {
          // 需要插件传参时候使用
      }
  
      /**
       * 在参数上下中自动加入数据权限过滤的字符串 ${dsf}
       *
       * @see <a>https://blog.csdn.net/lqzkcx3/article/details/80820327</a>
       *      参考{@link org.apache.ibatis.scripting.xmltags.DynamicContext}中{@code ContextMap} 的实现
       */
      class WrapContextMap extends HashMap<String, Object> {
  
          private final Object parameter;
          private final MetaObject metaObject;
  
          public WrapContextMap(Object parameter) {
              this.parameter = parameter;
              if (null != parameter) {
                  this.metaObject = SystemMetaObject.forObject(parameter);
              } else {
                  this.metaObject = null;
              }
          }
  
          @Override
          public Object get(Object key) {
              String strKey = (String)key;
              if (super.containsKey(strKey)) {
                  return super.get(strKey);
              }
  
              if (null == parameter) {
                  return null;
              }
  
              if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
                  return parameter;
              }
  
              if (metaObject != null) {
                  Object object = metaObject.getValue(strKey);
                  return object;
              }
  
              return null;
          }
      }
      // @formatter:off
      /**          sql 改写后替换原始MappedStatement.
       *            BoundSql boundSql = ms.getBoundSql(parameter);
       *            String sql = boundSql.getSql();
       *            log.info("rewrite sql, origin sql: [{}], new sql: [{}]", sql, newSql);
       *             BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), newSql,
       *                     boundSql.getParameterMappings(), boundSql.getParameterObject());
       *             // copy原始MappedStatement的各项属性
       *             MappedStatement.Builder builder =
       *                     new MappedStatement.Builder(mappedStatement.getConfiguration(), mappedStatement.getId(),
       *                             new WarpBoundSqlSqlSource(newBoundSql), mappedStatement.getSqlCommandType());
       *             builder.cache(mappedStatement.getCache()).databaseId(mappedStatement.getDatabaseId())
       *                     .fetchSize(mappedStatement.getFetchSize())
       *                     .flushCacheRequired(mappedStatement.isFlushCacheRequired())
       *                     .keyColumn(StringUtils.join(mappedStatement.getKeyColumns(), ','))
       *                     .keyGenerator(mappedStatement.getKeyGenerator())
       *                     .keyProperty(StringUtils.join(mappedStatement.getKeyProperties(), ','))
       *                     .lang(mappedStatement.getLang()).parameterMap(mappedStatement.getParameterMap())
       *                     .resource(mappedStatement.getResource()).resultMaps(mappedStatement.getResultMaps())
       *                     .resultOrdered(mappedStatement.isResultOrdered())
       *                     .resultSets(StringUtils.join(mappedStatement.getResultSets(), ','))
       *                     .resultSetType(mappedStatement.getResultSetType()).statementType(mappedStatement.getStatementType())
       *                     .timeout(mappedStatement.getTimeout()).useCache(mappedStatement.isUseCache());
       *             MappedStatement newMappedStatement = builder.build();
       *             // 将新生成的MappedStatement对象替换到参数列表中
       *             args[0] = newMappedStatement;
       */
      // @formatter:on
  }
  