Backend Development 22 min read

Design and Implementation of a Flexible Data Permission System in Java

This article presents a comprehensive design and implementation of a customizable data permission framework for Java backend applications, detailing the requirements, rule table structure, strategy pattern handling, custom annotations, AOP integration, and practical examples with full source code snippets.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Design and Implementation of a Flexible Data Permission System in Java

The author, a Java developer working in a small company, describes the challenges of implementing fine‑grained data permissions beyond the typical five built‑in options (view all, custom, department, department and below, self only) and proposes a flexible solution based on database rules and runtime expression evaluation.

Requirements

Requirement 1: Restrict a role (Role A) to view only specific enterprise types (e.g., catering, operation) under certain units, requiring a custom rule rather than the generic options.

Requirement 2: Role A can view only unpaid orders, while Role B can view orders with transaction amounts between 100 and 1000.

These scenarios cannot be satisfied by the existing five permission types.

Design Idea

The solution is inspired by a design article on data permission implementation. It stores permission rules in a database table sys_rule with fields such as id , remark , table_alias , column_name , splice_type , expression , provide_type , value1 , value2 , class_name , method_name , formal_param , and actual_param . The rule table drives the generation of SQL fragments for data filtering.

CREATE TABLE `sys_rule` (
  `id` bigint NOT NULL,
  `remark` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci COMMENT '备注',
  `mark_id` bigint DEFAULT NULL,
  `table_alias` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '表别名',
  `column_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '数据库字段名',
  `splice_type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '拼接类型 SpliceTypeEnum',
  `expression` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '表达式 ExpressionEnum',
  `provide_type` tinyint DEFAULT NULL COMMENT 'ProvideTypeEnum 值提供类型,1-值,2-方法',
  `value1` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '值1',
  `value2` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '值2',
  `class_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '全限定类名',
  `method_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '方法名',
  `formal_param` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '形参,分号隔开',
  `actual_param` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '实参,分号隔开',
  `create_time` datetime DEFAULT NULL,
  `create_by` bigint DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `update_by` bigint DEFAULT NULL,
  `deleted` bit(1) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='规则表';

The core processing is performed by DataScopeHandler , which implements DataPermissionHandler . It loads all applicable rules from the thread‑local DataScopeParam , iterates over them, and delegates each rule to an ExpressStrategy implementation based on the expression (e.g., EQ, NE, GT, BETWEEN).

@Component
public class DataScopeHandler implements DataPermissionHandler {
    Map
expressStrategyMap = new HashMap<>();

    @PostConstruct
    public void init() {
        expressStrategyMap.put(ExpressionEnum.EQ.toString(), new EqStrategyImpl());
        expressStrategyMap.put(ExpressionEnum.NE.toString(), new NeStrategyImpl());
        // ... other strategies
    }

    @Override
    public Expression getSqlSegment(Expression oldWhere, String mappedStatementId) {
        DataScopeParam dataScopeParam = DataScopeAspect.getDataScopeParam();
        if (dataScopeParam == null || dataScopeParam.getDataScopeInfo() == null
                || CollectionUtil.isEmpty(dataScopeParam.getDataScopeInfo().getRuleList())
                || SecurityUtil.isAdmin()) {
            return oldWhere;
        }
        // build new where clause using strategies
        // ...
    }
}

The ExpressStrategy interface defines the contract for applying a rule to the current Expression . An example implementation for the equality operator is shown below.

public class EqStrategyImpl implements ExpressStrategy {
    @Override
    public Expression apply(RuleDto rule, Expression where) {
        boolean or = isOr(rule.getSpliceType());
        Column column = getColumn(rule);
        Object value = getValue(rule);
        EqualsTo equalsTo = new EqualsTo(column, new StringValue((String) value));
        if (or) {
            where = where == null ? equalsTo : new OrExpression(where, equalsTo);
        } else {
            where = where == null ? equalsTo : new AndExpression(where, equalsTo);
        }
        return where;
    }
}

A custom annotation @DataScope marks methods or parameters that require data permission processing. An AOP aspect ( DataScopeAspect ) intercepts annotated methods, retrieves the corresponding rule set by the annotation value, stores it in a thread‑local, and clears it after execution.

@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope {
    /** The identifier of the permission rule set */
    String value();
}

Example 1 demonstrates how to configure a rule that limits a role to view orders with amount > 100 and < 500 by invoking a service method limitAmountBetween(BigDecimal, BigDecimal) via reflection. The result (order IDs) is injected into the SQL IN clause.

Example 2 shows a fuzzy address search using a simple value‑based rule without any custom code.

Beyond permission handling, the article also provides a set of generic CRUD base classes ( EntityController , EntityService , EntityServiceImpl ) that automatically generate REST endpoints for any entity, with support for pagination, sorting, and field selection via annotations such as @SelectColumn , @Query , @QuerySort , and @QueryOrder . These base classes illustrate how the permission framework integrates seamlessly with typical Spring‑Boot + MyBatis‑Plus projects.

Finally, the author shares the open‑source repository wonder-server where the full implementation can be explored, and invites contributors to join the maintenance effort.

JavaStrategy PatternAOPSpringCustom AnnotationMyBatisPlusData 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.