Information Security 11 min read

Implementing Data Masking in MySQL and Java with MyBatis-Mate Sensitive Jackson

This article demonstrates how to mask sensitive data such as phone numbers and ID cards using SQL functions, introduces Java masking libraries, and provides a complete MyBatis-Mate Sensitive Jackson example with configuration, custom strategies, entity annotations, controller endpoints, and sample responses.

Top Architect
Top Architect
Top Architect
Implementing Data Masking in MySQL and Java with MyBatis-Mate Sensitive Jackson

1. SQL Data Masking Implementation

Shows how to mask phone numbers and ID cards in MySQL using CONCAT, LEFT and RIGHT functions.

-- CONCAT(), LEFT() and RIGHT() string functions combined usage
-- CONCAT(str1,str2,…) returns the concatenated string
-- LEFT(str,len) returns the leftmost len characters of str
-- RIGHT(str,len) returns the rightmost len characters of str
-- Phone number masking SQL:
SELECT mobilePhone AS originalPhone,
       CONCAT(LEFT(mobilePhone,3), '********') AS maskedPhone
FROM t_s_user;
-- ID card masking SQL:
SELECT idcard AS originalIdCard,
       CONCAT(LEFT(idcard,3), '****', RIGHT(idcard,4)) AS maskedIdCard
FROM t_s_user;

2. Java Data Masking Implementation

References the open‑source project sensitive-plus which provides plugins for address, bank card, Chinese name, landline, ID card, mobile, email and password masking, supporting both regex‑based and length‑based strategies.

3. mybatis-mate-sensitive-jackson

Introduces the mybatis‑mate‑sensitive‑jackson library (commercial) that applies masking strategies defined by SensitiveType to entity fields.

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";
}

Configuration files (pom.xml, application.yml) and the Spring Boot startup class are provided.

<?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>
# DataSource Config
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:
    # Add your license here; test license will expire.
    grant: thisIsTestLicense
    license: as/bsBaSVrsA9FfjC/N77ruEt2/QZDrW+MHETNuEuZBra5mlaXZU+DE1ZvF8UjzlLCpH3TFVH3WPV+Ya7Ugiz1Rx4wSh/FK6Ug9lhos7rnsNaRB/+mR30aXqtlLt4dAmLAOCT56r9mikW+t1DDJY8TVhERWMjEipbqGO9oe1fqYCegCEX8tVCpToKr5J1g1V86mNsNnEGXujnLlEw9jBTrGxAyQroD7Ns1Dhwz1K4Y188mvmRQp9t7OYrpgsC7N9CXq1s1c2GtvfItHArkqHE4oDrhaPjpbMjFWLI5/XqZDtW3D+AVcH7pTcYZn6vzFfDZEmfDFV5fQlT3Rc+GENEg==
logging:
  level:
    mybatis.mate: debug
package mybatis.mate.sensitive.jackson;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SensitiveJacksonApplication {
    // Test endpoints: http://localhost:8080/info , http://localhost:8080/list
    public static void main(String[] args) {
        SpringApplication.run(SensitiveJacksonApplication.class, args);
    }
}

Custom strategy bean is defined to handle a user‑defined “testStrategy”.

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 custom masking strategy */
    @Bean
    public ISensitiveStrategy sensitiveStrategy() {
        // Custom "testStrategy" handling
        return new SensitiveStrategy().addStrategy("testStrategy", t -> t + "***test***");
    }
}

Entity class User demonstrates usage of @FieldSensitive with built‑in and custom strategies.

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;
    /** Custom strategy defined in SensitiveStrategyConfig */
    @FieldSensitive("testStrategy")
    private String username;
    /** Built‑in strategy for mobile numbers */
    @FieldSensitive(SensitiveType.mobile)
    private String mobile;
    @FieldSensitive(SensitiveType.email)
    private String email;
}

Controller provides endpoints /info, /map, /list with optional skipping of masking via the request parameter skip .

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;
    // GET http://localhost:8080/info
    @GetMapping("/info")
    public User info() {
        return userMapper.selectById(1L);
    }
    // GET http://localhost:8080/map
    @GetMapping("/map")
    public Map
map() {
        Map
userMap = new HashMap<>();
        userMap.put("user", userMapper.selectById(1L));
        userMap.put("test", 123);
        userMap.put("userMap", new HashMap
() {{
            put("user2", userMapper.selectById(2L));
            put("test2", "hi china");
        }});
        // Manual strategy masking
        userMap.put("mobile", sensitiveStrategy.getStrategyFunctionMap()
                .get(SensitiveType.mobile).apply("15315388888"));
        return userMap;
    }
    // GET http://localhost:8080/list (masking) or /list?skip=1 (no masking)
    @GetMapping("/list")
    public List
list(HttpServletRequest request) {
        if ("1".equals(request.getParameter("skip"))) {
            // Skip masking
            RequestDataTransfer.skipSensitive();
        }
        return userMapper.selectList(null);
    }
}

Mapper interface for MyBatis.

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
{}

Sample HTTP responses show masked data when the endpoint is called normally and original data when masking is skipped.

[
  {
    "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"
  }
]

[  // when skip=1
  {
    "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]"
  }
]
JavaSQLMyBatissecuritydata masking
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.