Backend Development 11 min read

Master Dynamic Data Permissions in Java with easy-data-scope

This tutorial walks you through building a Spring Boot project that uses the easy-data-scope library to inject dynamic SQL for fine‑grained data permissions, covering database setup, Maven dependencies, core annotations, custom rule implementation, and practical query examples.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Master Dynamic Data Permissions in Java with easy-data-scope

Introduction

easy-data-scope is a data‑permission project that injects SQL dynamically. It supports MyBatis, MyBatis‑plus, and MyBatis‑flex, and works with simple annotations without complex configuration.

Basic Project Setup

1. Database

A simple

user

table is created to demonstrate various data‑permission scenarios.

Only view records with id = 1

Only view records with age = 111

Only view records with age = 222

View records with age = 111 or 222

2. Import Basic Dependencies (MyBatis‑plus, MyBatis XML)

<code>&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter&lt;/artifactId&gt;
        &lt;version&gt;2.2.1.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
        &lt;version&gt;2.2.1.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.baomidou&lt;/groupId&gt;
        &lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;
        &lt;version&gt;3.3.0&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.mysql&lt;/groupId&gt;
        &lt;artifactId&gt;mysql-connector-j&lt;/artifactId&gt;
        &lt;version&gt;8.0.33&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;</code>

3. Core Dependency

<code>&lt;dependency&gt;
    &lt;groupId&gt;cn.zlinchuan&lt;/groupId&gt;
    &lt;artifactId&gt;ds-mybatis&lt;/artifactId&gt;
    &lt;version&gt;1.0.1&lt;/version&gt;
&lt;/dependency&gt;</code>

4. Main Class

<code>@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }
}</code>

5. Omitted Mapper and Service

Implementation details are omitted for brevity.

6. application.yml

<code>server:
  port: 8001
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/yourdb
    username: youruser
    password: yourpass
mybatis:
  mapper-locations: classpath:mapper/*.xml
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl</code>

Using easy-data-scope

Implement the core interface

DataScopeFindRule

and let Spring manage it.

The library intercepts methods annotated with

@DataScope

and retrieves

DataScopeInfo

objects via the

find()

method.

DataScopeInfo Introduction

easy-data-scope

builds SQL based on the list of

DataScopeInfo

returned by

find()

.

@DataScope Introduction

Place the annotation on methods that require data‑permission filtering.

DataScope Annotation Attributes

<code>public @interface DataScope {
    /** Keys used to fetch permission entities */
    String[] keys();
    /** Template for building SQL when multiple keys are present */
    String template() default "";
    /** Whether to merge permissions with the same column/operator */
    boolean merge() default false;
    /** Logical connector (WHERE, AND, OR, etc.) */
    String logical() default SqlConsts.AND;
    /** Whether to use the data‑scope flag placeholder */
    boolean flag() default false;
}</code>

Implementing DataScopeFindRule

<code>@Override
public List<DataScopeInfo> find(String[] key) {
    UserSessionInfo userSession = UserSessionContext.getUserSession();
    if (userSession != null) {
        QueryWrapper&lt;AuthDatascopeEntity&gt; qw = new QueryWrapper<>();
        qw.in("id", userSession.getDataScopeIds());
        qw.in("datascope_key", key);
        List&lt;AuthDatascopeEntity&gt; list = authDataSocpeMapper.selectList(qw);
        List&lt;DataScopeInfo&gt; infos = new ArrayList<>(list.size());
        for (AuthDatascopeEntity e : list) {
            DataScopeInfo i = new DataScopeInfo();
            i.setKey(e.getDatascopeKey());
            i.setOperator(e.getDatascopeOpName());
            i.setTableName(e.getDatascopeTbName());
            i.setColumnName(e.getDatascopeColName());
            i.setSql(e.getDatascopeSql());
            i.setValue(e.getDatascopeValue());
            i.setSort(e.getDatascopeSort());
            infos.add(i);
        }
        return infos;
    }
    return Collections.emptyList();
}</code>

Creating the Data Permission Table

<code>CREATE TABLE auth_datascope (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '编号',
    datascope_key VARCHAR(200) NULL COMMENT '数据权限标识',
    datascope_name VARCHAR(200) NULL COMMENT '数据权限名称',
    datascope_tb_name VARCHAR(500) NULL COMMENT '数据权限表别名',
    datascope_col_name VARCHAR(500) NULL COMMENT '数据权限字段名',
    datascope_op_name VARCHAR(10) NULL COMMENT '数据权限操作符',
    datascope_sql VARCHAR(5000) NULL COMMENT '数据权限SQL',
    datascope_value VARCHAR(200) NULL COMMENT '数据权限值',
    datascope_sort INT NULL COMMENT '数据权限排序',
    datascope_des VARCHAR(500) NULL COMMENT '数据权限描述'
) COMMENT '数据权限表';</code>

Permission Scenarios

1. Only view records with id = 1

<code>@DataScope(keys = "USER_LIST_ID", logical = SqlConsts.WHERE)
public List<UserEntity> getAll() {
    return userMapper.selectList(null);
}</code>

Generated SQL:

<code>SELECT id, username, age FROM user WHERE (user.id = 1)</code>

2. Only view records with age = 111

<code>@DataScope(keys = "USER_LIST_AGE111", logical = SqlConsts.WHERE)
public List<UserEntity> getAll2() {
    return userMapper.selectList(null);
}</code>
<code>SELECT id, username, age FROM user WHERE (user.age = 111)</code>

3. Only view records with age = 222

<code>@DataScope(keys = "USER_LIST_AGE222", logical = SqlConsts.WHERE)
public List<UserEntity> getAll3() {
    return userMapper.selectList(null);
}</code>
<code>SELECT id, username, age FROM user WHERE (user.age = 222)</code>

4. View records with age = 111 OR 222 (merge)

<code>@DataScope(keys = {"USER_LIST_AGE111", "USER_LIST_AGE222"}, merge = true, logical = SqlConsts.WHERE)
public List<UserEntity> getAll4() {
    return userMapper.selectList(null);
}</code>
<code>SELECT id, username, age FROM user WHERE (user.age IN (111, 222))</code>

Advanced Operations

@DataScope.flag

<code>@DataScope(keys = {"USER_LIST_AGE111", "USER_LIST_AGE222"}, merge = true, flag = true)
List<UserEntity> getAll5();</code>

Mapper XML uses the placeholder

{{_DATA_SCOPE_FLAG}}

which must not be altered.

@DataScope.template

<code>@DataScope(keys = {"USER_LIST_AGE111", "USER_LIST_AGE222"}, flag = true, template = "{{USER_LIST_AGE111}} OR {{USER_LIST_AGE222}}")
List<UserEntity> getAll6();</code>
<code>SELECT * FROM (SELECT * FROM user WHERE user.age = 111 OR user.age = 222) t WHERE 1 = 1</code>

Project Source

https://github.com/zoulinchuan/easy-data-scope

Javabackend developmentSpring BootMyBatisData Permissioneasy-data-scope
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

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.