Backend Development 9 min read

Design and Implementation of Data Permission in Backend Projects Using MyBatis Interceptor and Spring Boot Auto‑Configuration

This article explains a low‑intrusion data‑permission design for backend micro‑services, detailing resource and value‑rule definitions, RBAC modeling, MyBatis SQL interception implementation, Redis‑based permission retrieval, and Spring Boot auto‑configuration for easy enablement in production.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Design and Implementation of Data Permission in Backend Projects Using MyBatis Interceptor and Spring Boot Auto‑Configuration

Background – In many small backend projects, developers often hard‑code data‑permission filters directly into SQL queries. While this works for a single module, it becomes invasive and labor‑intensive when the system is split into micro‑services, requiring changes in every module.

Data‑Permission Design – The design aims for low or zero code intrusion and one‑click enable/disable. Permissions are expressed as resources (e.g., a table column such as createUserId ) and value‑rules that define how the column is filtered (e.g., createUserId=1 for “own records”).

Detailed Design

Use an RBAC (Role‑Based Access Control) model.

Resource design links a request URL → mapper method → database table, allowing permission checks only on relevant queries.

Value‑rule types include: own‑record, department‑visible, department‑and‑sub‑departments, specific value, and custom SQL.

Examples: Own‑record: Retrieve the current user ID via a full‑path Spring bean method and append createUserId=1 to the SQL. Specific value: For a group leader to see all orders of their group, use a predefined value. Custom: Use a custom SQL fragment such as order_amount>100000 or a custom method to fetch company‑wide IDs.

SQL Execution Interception – Implemented with MyBatis. The interceptor follows the pattern of MyBatis PageHelper, intercepting the Executor.query method, extracting the bound SQL, and delegating to a helper that appends permission conditions.

@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})
})
@Slf4j
public class DataPermissionsHandlerInterceptor implements Interceptor {
    @Resource(name = "org.demo.common.permission.dialect.RedisMysqlDataPermissionDialect")
    private DataPermissionDialect dialect;

    @Override
    public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
        try {
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement) args[0];
            Object parameter = args[1];
            RowBounds rowBounds = (RowBounds) args[2];
            ResultHandler resultHandler = (ResultHandler) args[3];
            Executor executor = (Executor) invocation.getTarget();
            CacheKey cacheKey;
            BoundSql boundSql;
            if (args.length == 4) {
                boundSql = ms.getBoundSql(parameter);
                cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
            } else {
                cacheKey = (CacheKey) args[4];
                boundSql = (BoundSql) args[5];
            }
            List
allTableName = null;
            Map
> dataPermission = null;
            return ExecuteSqlContext.doResponsibility(invocation, ms, parameter, rowBounds, resultHandler, cacheKey, boundSql, executor, allTableName, dialect, dataPermission);
        } catch (Exception e) {
            log.error("数据权限拦截sql时异常:{}", e);
            return invocation.proceed();
        }
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Interceptor.super.plugin(target);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}

Current User Permission Retrieval – User permission data is stored in Redis. A Redis key is composed of userId + mapperMethod + requestUrl , and the value is a hash where each field is a table name mapping to its permission rule. The interceptor reads this key to decide whether to modify the SQL.

One‑Click Enable/Disable (Auto‑Configuration) – A custom Spring Boot starter provides the @EnableDataPermission annotation. Adding this annotation to the main application class automatically registers the interceptor, permission dialects, and related beans.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({DataPermissionAutoImport.class})
public @interface EnableDataPermission {}
public class DataPermissionAutoImport implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{
            "org.demo.common.permission.dialect.MySqlDataPermissionDialect",
            "org.demo.common.permission.dialect.RedisDataPermissionDialect",
            "org.demo.common.permission.dialect.RedisMysqlDataPermissionDialect",
            "org.demo.common.permission.interceptor.DataPermissionsHandlerInterceptor",
            "org.demo.common.permission.auto.ClassMethodGetValueBeanRegistry"
        };
    }
}

Conclusion – This low‑intrusion design works well for small‑scale projects, but as the system grows, permission configuration can become cumbersome and may require more sophisticated management tools.

Backend DevelopmentSpring BootMyBatisSQL InterceptorRBACData Permission
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.