Three Practical Data Masking Solutions That Really Work

This article walks through three common data‑masking approaches—SQL queries using string functions, a Java‑based "sensitive‑plus" plugin, and the mybatis‑mate‑sensitive‑jackson extension—showing configuration, code examples, and how each method masks phone numbers, ID cards and other personal fields.

Architect's Guide
Architect's Guide
Architect's Guide
Three Practical Data Masking Solutions That Really Work

1. SQL Data Masking

Using MySQL string functions CONCAT(), LEFT() and RIGHT(), the article demonstrates how to mask phone numbers and ID numbers directly in SQL queries.

-- Phone number masking
SELECT mobilePhone AS original_phone,
       CONCAT(LEFT(mobilePhone,3),'********') AS masked_phone
FROM t_s_user;

-- ID card masking
SELECT idcard AS original_id,
       CONCAT(LEFT(idcard,3),'****',RIGHT(idcard,4)) AS masked_id
FROM t_s_user;

2. Java Data Masking with sensitive‑plus

The open‑source plugin hosted at https://gitee.com/strong_sea/sensitive-plus supports address, bank‑card, Chinese name, landline, ID card, mobile phone, and password masking. It offers both regex‑based and length‑based masking, configurable via custom rules.

3. mybatis‑mate‑sensitive‑jackson

A MyBatis‑Plus extension (free for testing, paid for production) that applies masking strategies defined by SensitiveType. The interface lists built‑in types such as chineseName, idCard, phone, mobile, address, email, bankCard, password, and carNumber.

package mybatis.mate.strategy;

public interface SensitiveType {
    String chineseName = "chineseName";
    String idCard = "idCard";
    String phone = "phone";
    String mobile = "mobile";
    String address = "address";
    String email = "email";
    String bankCard = "bankCard";
    String password = "password";
    String carNumber = "carNumber";
}

The accompanying demo includes the following files:

pom.xml – adds the MySQL connector dependency.

application.yml – configures the datasource and a test license for mybatis‑mate.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-mate-examples</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>mybatis-mate-sensitive-jackson</artifactId>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
</project>
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_mate?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
mybatis-mate:
  cert:
    grant: thisIsTestLicense
    license: <long‑base64‑string>
logging:
  level:
    mybatis.mate: debug

4. Spring Boot Application Entry

package mybatis.mate.sensitive.jackson;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SensitiveJacksonApplication {
    public static void main(String[] args) {
        SpringApplication.run(SensitiveJacksonApplication.class, args);
    }
}

5. Custom Masking Strategy Configuration

package mybatis.mate.sensitive.jackson.config;

import mybatis.mate.databind.ISensitiveStrategy;
import mybatis.mate.strategy.SensitiveStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SensitiveStrategyConfig {
    /** Inject a custom masking strategy */
    @Bean
    public ISensitiveStrategy sensitiveStrategy() {
        return new SensitiveStrategy().addStrategy(
            "testStrategy", t -> t + "***test***");
    }
}

6. Entity Definition

package mybatis.mate.sensitive.jackson.entity;

import lombok.Getter;
import lombok.Setter;
import mybatis.mate.annotation.FieldSensitive;
import mybatis.mate.strategy.SensitiveType;

@Getter
@Setter
public class User {
    private Long id;
    @FieldSensitive("testStrategy")
    private String username; // uses custom strategy
    @FieldSensitive(SensitiveType.mobile)
    private String mobile;   // built‑in mobile masking
    @FieldSensitive(SensitiveType.email)
    private String email;    // built‑in email masking
}

7. REST Controller

package mybatis.mate.sensitive.jackson.controller;

import mybatis.mate.databind.ISensitiveStrategy;
import mybatis.mate.databind.RequestDataTransfer;
import mybatis.mate.sensitive.jackson.entity.User;
import mybatis.mate.sensitive.jackson.mapper.UserMapper;
import mybatis.mate.strategy.SensitiveType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ISensitiveStrategy sensitiveStrategy;

    // Retrieve a single user
    @GetMapping("/info")
    public User info() {
        return userMapper.selectById(1L);
    }

    // Return a map with nested objects and manual masking
    @GetMapping("/map")
    public Map<String, Object> map() {
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("user", userMapper.selectById(1L));
        userMap.put("test", 123);
        userMap.put("userMap", new HashMap<String, Object>() {{
            put("user2", userMapper.selectById(2L));
            put("test2", "hi china");
        }});
        // Manually apply mobile masking
        userMap.put("mobile", sensitiveStrategy.getStrategyFunctionMap()
                .get(SensitiveType.mobile).apply("15315388888"));
        return userMap;
    }

    // List users, optionally skipping masking with ?skip=1
    @GetMapping("/list")
    public List<User> list(HttpServletRequest request) {
        if ("1".equals(request.getParameter("skip"))) {
            RequestDataTransfer.skipSensitive();
        }
        return userMapper.selectList(null);
    }
}

8. Mapper Interface

package mybatis.mate.sensitive.jackson.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import mybatis.mate.sensitive.jackson.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {}

9. Test Results

Request GET http://localhost:8080/list returns masked fields:

[
  {"id":1,"username":"Jone***test***","mobile":"153******81","email":"t****@baomidou.com"},
  {"id":2,"username":"Jack***test***","mobile":"153******82","email":"t****@baomidou.com"},
  {"id":3,"username":"Tom***test***","mobile":"153******83","email":"t****@baomidou.com"}
]

Adding the query parameter ?skip=1 disables masking and returns the original data:

[
  {"id":1,"username":"Jone","mobile":"15315388881","email":"[email protected]"},
  {"id":2,"username":"Jack","mobile":"15315388882","email":"[email protected]"},
  {"id":3,"username":"Tom","mobile":"15315388883","email":"[email protected]"}
]
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaSQLspring-bootMyBatisdata maskingmybatis-matesensitive-plus
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

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.