SoFunction
Updated on 2025-03-08

Three ways to implement MyBatisPlus data permission control

1. Scene

When controlling data permissions, it is hoped that it is global. For example, each sql is queried based on the current login, so it is hoped that each sql will be spliced ​​at the end "create_user = 'admin'"

2. Implementation method (provided two types, choose by yourself)

Note: Each table must ensure that the field of create_user exists, otherwise the ignoreTable method must be overridden.

2.1 Imitate the implementation method of tenants

@Bean
public MybatisPlusInterceptor paginationInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
    ();
    (1000L);
    (true);
    (paginationInnerInterceptor);
    (new CustomLineInnerInterceptor(new () {
        @Override
        public Expression getCreateUser() {
            // Write the dead admin here directly, and get it on demand            return new StringValue("admin");
        }

        @Override
        public String getCreateUserColumn() {
            return ();
        }

        @Override
        public boolean ignoreTable(String tableName) {
            //This is a case. When multiple tables need to be filtered, they can be obtained from the configuration file or static code block.            List<String> ignoreTables = ("sys_model_relation");
            //Judge whether the current table is in the filter list            return ().anyMatch(tableName::equalsIgnoreCase);
        }
    }));
    return interceptor;
}
/**
 * @description:
 * @author: SmallNorth_Lee
 * @date: 2024/5/7 15:29
 * @version: 1.0
 */
public interface DataPermissionHandler {

    /**
      * Get user ID value expression, only a single ID value is supported
      * <p>
      *
      * @return User ID value expression
      */
    Expression getCreateUser();

    /**
      * Get user field name
      * <p>
      * The default field name is: create_user
      *
      * @return User field name
      */
    default String getCreateUserColumn() {
        return "create_user";
    }

    /**
      * Judging whether to ignore the splicing multi-user conditions based on the table name
      * <p>
      * By default, you must parse and splice multi-user conditions
      *
      * @param tableName table name
      * @return Ignore whether it is ignored, true: means ignore, false: It needs to be parsed and spliced ​​with multi-user conditions
      */
    default boolean ignoreTable(String tableName) {
        return false;
    }

    /**
      * Ignore the insert user field logic
      *
      * @param columns Insert field
      * @param createUserColumn User ID field
      * @return
      */
    default boolean ignoreInsert(List&lt;Column&gt; columns, String createUserColumn) {
        return ().map(Column::getColumnName).anyMatch(i -&gt; (createUserColumn));
    }
}
/**
 * @description:
 * @author: SmallNorth_Lee
 * @date: 2024/5/7 14:14
 * @version: 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class CustomLineInnerInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {

    private DataPermissionHandler dataPermissionHandler;

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        if ((())) {
            return;
        }
         mpBs = (boundSql);
        (parserSingle((), null));
    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
         mpSh = (sh);
        MappedStatement ms = ();
        SqlCommandType sct = ();
        if (sct ==  || sct ==  || sct == ) {
            if ((())) {
                return;
            }
             mpBs = ();
            (parserMulti((), null));
        }
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        final String whereSegment = (String) obj;
        processSelectBody((), whereSegment);
        List&lt;WithItem&gt; withItemsList = ();
        if (!(withItemsList)) {
            (withItem -&gt; processSelectBody(withItem, whereSegment));
        }
    }

    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
        if ((().getName())) {
            // Filter Exit Execution            return;
        }
        List&lt;Column&gt; columns = ();
        if ((columns)) {
            // No processing for inserts that do not give column names            return;
        }
        String createUserColumn = ();
        if ((columns, createUserColumn)) {
            // No processing for inserts given by the given tenant column            return;
        }
        (new Column(createUserColumn));

        // fixed gitee pulls/141 duplicate update
        List&lt;Expression&gt; duplicateUpdateColumns = ();
        if ((duplicateUpdateColumns)) {
            EqualsTo equalsTo = new EqualsTo();
            (new StringValue(createUserColumn));
            (());
            (equalsTo);
        }

        Select select = ();
        if (select != null &amp;&amp; (() instanceof PlainSelect)) { //fix github issue 4998 Fixed the issue of upgrading to version 4.5            ((), (String) obj);
        } else if (() != null) {
            // fixed github pull/295
            ItemsList itemsList = ();
            Expression createUser = ();
            if (itemsList instanceof MultiExpressionList) {
                ((MultiExpressionList) itemsList).getExpressionLists().forEach(el -&gt; ().add(createUser));
            } else {
                List&lt;Expression&gt; expressions = ((ExpressionList) itemsList).getExpressions();
                if ((expressions)) {//fix github issue 4998 jsqlparse 4.5 batch insert ItemsList is not a MultiExpressionList, and it requires special processing                    int len = ();
                    for (int i = 0; i &lt; len; i++) {
                        Expression expression = (i);
                        if (expression instanceof RowConstructor) {
                            ((RowConstructor) expression).getExprList().getExpressions().add(createUser);
                        } else if (expression instanceof Parenthesis) {
                            RowConstructor rowConstructor = new RowConstructor()
                                    .withExprList(new ExpressionList(((Parenthesis) expression).getExpression(), createUser));
                            (i, rowConstructor);
                        } else {
                            if (len - 1 == i) { // (?,?) TenantId is spliced ​​only when the last expre is                                (createUser);
                            }
                        }
                    }
                } else {
                    (createUser);
                }
            }
        } else {
            throw ("Failed to process multiple-table update, please exclude the tableName or statementId");
        }
    }

    /**
      * update statement processing
      */
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        final Table table = ();
        if ((())) {
            // Filter Exit Execution            return;
        }
        ArrayList&lt;UpdateSet&gt; sets = ();
        if (!(sets)) {
            (us -&gt; ().forEach(ex -&gt; {
                if (ex instanceof SubSelect) {
                    processSelectBody(((SubSelect) ex).getSelectBody(), (String) obj);
                }
            }));
        }
        ((table, (), (String) obj));
    }

    /**
      * delete statement processing
      */
    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        if ((().getName())) {
            // Filter Exit Execution            return;
        }
        (((), (), (String) obj));
    }

    /**
      * Process insert into select
      * <p>
      * Entering here means that the table that requires insert has enabled multi-tenants, and then the selected tables are started
      *
      * @param selectBody SelectBody
      */
    protected void processInsertSelect(SelectBody selectBody, final String whereSegment) {
        PlainSelect plainSelect = (PlainSelect) selectBody;
        FromItem fromItem = ();
        if (fromItem instanceof Table) {
            // fixed gitee pulls/141 duplicate update
            processPlainSelect(plainSelect, whereSegment);
            appendSelectItem(());
        } else if (fromItem instanceof SubSelect) {
            SubSelect subSelect = (SubSelect) fromItem;
            appendSelectItem(());
            processInsertSelect((), whereSegment);
        }
    }

    /**
      * Add SelectItem
      *
      * @param selectItems SelectItem
      */
    protected void appendSelectItem(List&lt;SelectItem&gt; selectItems) {
        if ((selectItems)) {
            return;
        }
        if (() == 1) {
            SelectItem item = (0);
            if (item instanceof AllColumns || item instanceof AllTableColumns) {
                return;
            }
        }
        (new SelectExpressionItem(new Column(())));
    }

    /**
      * Tenant field alias settings
      * <p>tenantId or </p>
      *
      * @param table table object
      * @return field
      */
    protected Column getAliasColumn(Table table) {
        StringBuilder column = new StringBuilder();
        // todo If you want to alias, you must alias. Modification of this logic is prohibited.        if (() != null) {
            (().getName()).append();
        }
        (());
        return new Column(());
    }

    @Override
    public void setProperties(Properties properties) {
        (properties).whenNotBlank("dataPermissionHandler",
                ClassUtils::newInstance, this::setDataPermissionHandler);
    }

    /**
      * Build tenant conditional expressions
      *
      * @param table table object
      * @param where current where conditions
      * @param whereSegment The full path of the Mapper object to which it belongs (in the original tenant interceptor function, this parameter does not need to participate in relevant judgments)
      * @return Tenant conditional expression
      * @see BaseMultiTableInnerInterceptor#buildTableExpression(Table, Expression, String)
      */
    @Override
    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {
        if ((())) {
            return null;
        }
        return new EqualsTo(getAliasColumn(table), ());
    }
}

2.2 Using DataPermissionInterceptor and MultiDataPermissionHandler

        (new DataPermissionInterceptor(new MultiDataPermissionHandler() {
            @Override
            public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
                Column aliasColumn = getAliasColumn(table);
                if (null == aliasColumn) {
                    return null;
                }
                return new EqualsTo(getAliasColumn(table), new StringValue(()));
            }
        }));
    protected Column getAliasColumn(Table table) {
        //This is a case. When multiple tables need to be filtered, they can be obtained from the configuration file or static code block.        List&lt;String&gt; ignoreTables = ("sys_model_relation");
        //Judge whether the current table is in the filter list        boolean match = ().anyMatch(()::equalsIgnoreCase);
        if (match) {
            return null;
        }
        StringBuilder column = new StringBuilder();
        // todo If you want to alias, you must alias. Modification of this logic is prohibited.        if (() != null) {
            (().getName()).append();
        }
        ("create_user");
        return new Column(());
    }

This is the end of this article about the three ways to implement MyBatisPlus data permission control. For more related content on MyBatisPlus permission control, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!