| name | rpamis-security |
| description | MyBatis-based enterprise data security component for data masking and encryption/decryption. Invoke when implementing data masking, database encryption, SM4 encryption, or working with @Masked/@SecurityField annotations. |
Rpamis-Security Skill
Enterprise-grade MyBatis data security component providing annotation-based data masking and automatic database encryption/decryption with SM4 support.
๐ฏ When to Use This Skill
Invoke this skill when you need to:
- Implement data masking for sensitive information (names, phones, ID cards, emails, bank cards)
- Set up automatic database field encryption/decryption
- Work with SM4 (Chinese national standard) encryption algorithm
- Use
@Masked or @SecurityField annotations
- Customize masking rules or encryption algorithms
- Handle nested entity masking scenarios
- Troubleshoot encryption/decryption issues
- Migrate from older versions or similar security frameworks
๐ฆ Quick Integration
Step 1: Add Maven Dependency
For JDK 17+ (Recommended):
<dependency>
<groupId>com.rpamis</groupId>
<artifactId>rpamis-security-spring-boot-starter</artifactId>
<version>1.1.4</version>
</dependency>
For JDK 8-17:
<dependency>
<groupId>com.rpamis</groupId>
<artifactId>rpamis-security-spring-boot-starter</artifactId>
<version>1.0.7</version>
</dependency>
Step 2: Configure Application
application.yml (Complete Configuration):
rpamis:
security:
enable: true
ignore-decrypt-failed: true
desensitization-enable: true
custom-pointcut: '@within(org.springframework.web.bind.annotation.RestController)'
algorithm:
active: sm4
sm4:
key: 1234567890123456
prefix: ENC_SM4_
Configuration Parameters Explained:
| Parameter | Type | Default | Description |
|---|
enable | Boolean | false | Master switch for security component. When false, all features disabled |
ignore-decrypt-failed | Boolean | true | Return original value on decryption failure instead of throwing exception |
desensitization-enable | Boolean | true | Enable/disable masking AOP aspect |
custom-pointcut | String | "" | Custom AOP pointcut expression (e.g., RestController) |
algorithm.active | String | null | Active encryption algorithm (sm4) |
algorithm.sm4.key | String | null | SM4 encryption key (must be 16 characters) |
algorithm.sm4.prefix | String | ENC_SM4_ | Prefix to identify encrypted values |
Step 3: Use Annotations
For Data Masking:
import com.rpamis.security.annotation.Masked;
import com.rpamis.security.mask.MaskType;
public class UserVO {
@Masked(type = MaskType.NAME_MASK)
private String name;
@Masked(type = MaskType.PHONE_MASK)
private String phone;
@Masked(type = MaskType.IDCARD_MASK)
private String idCard;
}
For Nested Masking:
import com.rpamis.security.annotation.Masked;
import com.rpamis.security.annotation.NestedMasked;
import com.rpamis.security.mask.MaskType;
public class UserVO {
@Masked(type = MaskType.NAME_MASK)
private String name;
@NestedMasked
private ContactInfoVO contactInfo;
}
For Database Encryption:
import com.rpamis.security.annotation.SecurityField;
public class UserDO {
private Long id;
@SecurityField
private String password;
@SecurityField
private String idCard;
}
๐ญ Data Masking Guide
Built-in Masking Types
| MaskType | Input Example | Output Example | Use Case |
|---|
NO_MASK | ๅผ ไธ | ๅผ ไธ | No masking needed |
NAME_MASK | ๅผ ไธ | ๅผ * | User names |
PHONE_MASK | 13812345678 | 138****5678 | Phone numbers |
IDCARD_MASK | 110101199001011234 | 110101********1234 | ID cards |
EMAIL_MASK | example@domain.com | e****e@domain.com | Email addresses |
BANKCARD_MASK | 6222021234567890 | 6222****7890 | Bank cards |
ADDRESS_MASK | ๅไบฌๅธๆ้ณๅบ | ๅไบฌๅธ*** | Addresses |
ALL_MASK | ไปปๆๅ
ๅฎน | ****** | Complete masking |
CUSTOM_MASK | ABCDEFG | AB###FG | Custom patterns |
Custom Masking Example
public class UserVO {
@Masked(type = MaskType.CUSTOM_MASK, start = 2, end = 5, symbol = "#")
private String customField;
}
Result: ABCDEFG โ AB###FG
Nested Masking
Use @NestedMasked annotation to mark nested entity fields that need masking:
import com.rpamis.security.annotation.Masked;
import com.rpamis.security.annotation.NestedMasked;
import com.rpamis.security.mask.MaskType;
@Data
public class UserDetailVO implements Serializable {
private static final long serialVersionUID = 1L;
@Masked(type = MaskType.NAME_MASK)
private String name;
@NestedMasked
private ContactInfoVO contactInfo;
}
@Data
public class ContactInfoVO implements Serializable {
private static final long serialVersionUID = 1L;
@Masked(type = MaskType.PHONE_MASK)
private String phone;
@Masked(type = MaskType.EMAIL_MASK)
private String email;
@NestedMasked
private AddressVO address;
}
@Data
public class AddressVO implements Serializable {
private static final long serialVersionUID = 1L;
@Masked(type = MaskType.ADDRESS_MASK)
private String homeAddress;
@Masked(type = MaskType.ADDRESS_MASK)
private String officeAddress;
}
Controller Example:
@RestController
@RequestMapping("/api")
public class UserDetailController {
@PostMapping("/user/detail")
@Desensitizationed
public UserDetailVO getUserDetail() {
AddressVO address = new AddressVO();
address.setHomeAddress("ๅไบฌๅธๆ้ณๅบๅปบๅฝ่ทฏ88ๅท");
address.setOfficeAddress("ๅไบฌๅธๆตทๆทๅบไธญๅ
ณๆ");
ContactInfoVO contactInfo = new ContactInfoVO();
contactInfo.setPhone("13812345678");
contactInfo.setEmail("zhangsan@example.com");
contactInfo.setAddress(address);
UserDetailVO userDetail = new UserDetailVO();
userDetail.setName("ๅผ ไธ");
userDetail.setContactInfo(contactInfo);
return userDetail;
}
}
Output:
{
"name": "ๅผ *",
"contactInfo": {
"phone": "138****5678",
"email": "z****n@example.com",
"address": {
"homeAddress": "ๅไบฌๅธๆ้ณๅบ***",
"officeAddress": "ๅไบฌๅธๆตทๆทๅบ***"
}
}
}
Key Points:
- Use
@NestedMasked on nested entity fields
- Nested entities can also contain
@NestedMasked fields (multi-level nesting)
- All
@Masked fields in nested entities will be processed automatically
- Works with
@Desensitizationed annotation on controller methods
Enable Masking in Controller
Add @Desensitizationed annotation to controller methods:
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/info")
@Desensitizationed
public UserVO getUserInfo() {
return userService.getUser();
}
@GetMapping("/list")
@Desensitizationed
public List<UserVO> getUserList() {
return userService.getUserList();
}
}
Supported Return Types:
- Single entity:
UserVO
- List:
List<UserVO>
- Map:
Map<String, UserVO>
- Generic wrapper:
Response<UserVO>
- Non-generic wrapper:
Response
๐ Database Encryption Guide
How It Works
- Insert Operation: Automatically encrypts
@SecurityField annotated fields before saving
- Select Operation: Automatically decrypts encrypted fields after querying
- Update Operation: Encrypts new values, handles deep copy to preserve source object
- Deep Copy Design: Source object reference preserved after save operations
MyBatis-Plus Integration
@TableName("user")
public class UserDO {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField(value = "name")
@SecurityField
private String name;
@TableField(value = "id_card")
@SecurityField
private String idCard;
@TableField(value = "phone")
@SecurityField
private String phone;
}
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void saveUser(UserDO user) {
userMapper.insert(user);
}
public UserDO getUser(Long id) {
return userMapper.selectById(id);
}
}
MyBatis XML Integration
<mapper namespace="com.example.mapper.UserMapper">
<insert id="insert" parameterType="UserDO">
INSERT INTO user (name, id_card, phone)
VALUES (#{name}, #{idCard}, #{phone})
</insert>
<select id="selectById" resultType="UserDO">
SELECT id, name, id_card, phone
FROM user
WHERE id = #{id}
</select>
</mapper>
Handling Encrypted Data Updates
@PostMapping("/update")
public void updateUser() {
UserDO user = new UserDO();
user.setName("ๅผ ไธ");
user.setIdCard("500101111118181952");
userMapper.insert(user);
user.setName("ๆๅ");
userMapper.updateById(user);
UserDO result = userMapper.selectById(user.getId());
}
Wrapper Update with Manual Encryption
@PostMapping("/update/wrapper")
public void updateWithWrapper() {
UpdateWrapper<UserDO> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda()
.set(UserDO::getName, securityAlgorithm.encrypt("ๆๅ"))
.eq(UserDO::getId, userId);
userMapper.update(null, updateWrapper);
}
๐ง Advanced Features
Custom Encryption Algorithm
@Component
public class CustomAlgorithmImpl implements SecurityAlgorithm {
@Override
public String encrypt(String content) {
return encryptedContent;
}
@Override
public String decrypt(String content) {
return decryptedContent;
}
}
Custom Mask Function
@Component
public class CustomMaskFunction implements MaskFunction {
@Override
public String mask(String content) {
return maskedContent;
}
}
Check if Data is Encrypted
@Autowired
private SecurityUtils securityUtils;
public void checkEncryption(String value) {
boolean isEncrypted = securityUtils.checkHasBeenEncrypted(value);
}
๐งช Testing Guide
Unit Test Example
@SpringBootTest
public class SecurityTest {
@Autowired
private UserMapper userMapper;
@Test
public void testInsertAndDecrypt() {
UserDO user = new UserDO();
user.setName("ๅผ ไธ");
user.setIdCard("500101111118181952");
user.setPhone("12345678965");
userMapper.insert(user);
UserDO result = userMapper.selectById(user.getId());
assertEquals("ๅผ ไธ", result.getName());
assertEquals("500101111118181952", result.getIdCard());
assertEquals("12345678965", result.getPhone());
}
@Test
@Desensitizationed
public void testMasking() {
UserVO user = userService.getUser();
assertEquals("ๅผ *", user.getName());
assertEquals("500***********1952", user.getIdCard());
}
}
โก Performance Optimization
1. Batch Operations
List<UserDO> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
userService.saveBatch(userList);
2. Selective Field Encryption
Only encrypt sensitive fields:
public class UserDO {
private Long id;
private String username;
@SecurityField
private String password;
@SecurityField
private String idCard;
private Integer age;
}
3. Cache Isolation
The framework automatically isolates encryption/decryption cache to prevent interference:
@Test
public void testCacheIsolate() {
TestVersionV2DO encrypted = mapper.selectById(1);
TestVersionDO normal = new TestVersionDO();
normal.setName("ๅผ ไธ");
mapper.insert(normal);
}
๐ก๏ธ Security Best Practices
1. Key Management
DO NOT hardcode encryption keys in source code:
rpamis:
security:
algorithm:
sm4:
key: ${SM4_KEY:default-key-for-dev}
Use environment variables or secret management services.
2. Key Rotation
- Change encryption keys periodically
- Maintain key version history for backward compatibility
- Plan migration strategy for existing encrypted data
3. Access Control
- Limit access to encryption keys
- Use different keys for different environments (dev, test, prod)
- Audit key usage and access logs
4. Error Handling
rpamis:
security:
ignore-decrypt-failed: true
Set to false in production to detect tampering attempts.
๐ Troubleshooting
Issue 1: Encryption Not Working
Symptoms: Data stored in plaintext
Checklist:
- โ
rpamis.security.enable is true
- โ
@SecurityField annotation present on field
- โ Correct Maven dependency version
- โ SM4 key configured (16 characters)
Debug:
@Autowired
private SecurityAlgorithm securityAlgorithm;
String encrypted = securityAlgorithm.encrypt("test");
System.out.println(encrypted);
Issue 2: Masking Not Applied
Symptoms: Sensitive data returned without masking
Checklist:
- โ
rpamis.security.desensitization-enable is true
- โ
@Masked annotation present on field
- โ
@Desensitizationed annotation on controller method
- โ AOP enabled in Spring Boot (
@EnableAspectJAutoProxy)
Debug:
@GetMapping("/test")
@Desensitizationed
public UserVO test() {
UserVO user = new UserVO();
user.setName("ๅผ ไธ");
return user;
}
Issue 3: SM4 Encryption Errors
Symptoms: Encryption/decryption exceptions
Checklist:
- โ SM4 key is exactly 16 characters
- โ SM4 algorithm supported in JDK (JDK 8u161+ or JDK 11+)
- โ No special characters in key
Solution:
rpamis:
security:
algorithm:
sm4:
key: 1234567890123456
Issue 4: Decryption Returns Original Value
Symptoms: Encrypted data not decrypted
Possible Causes:
- Data not encrypted (missing
@SecurityField on insert)
- Prefix mismatch (custom prefix configured)
- Legacy data without prefix (version < 1.0.2)
Solution:
rpamis:
security:
ignore-decrypt-failed: true
Issue 5: Nested Decryption Not Working
Symptoms: Nested entity fields not decrypted
Checklist:
- โ Nested entity has
@SecurityField annotations
- โ MyBatis result mapping correct
Example:
public class OrderVO {
private UserDO user;
}
public class UserDO {
@SecurityField
private String idCard;
}
๐ Migration Guide
From Version 1.0.x to 1.1.x
Breaking Changes:
- JDK 17+ required for 1.1.4
- SM4 prefix added (
ENC_SM4_)
Migration Steps:
- Update Maven dependency to 1.1.4
- Configure SM4 prefix in application.yml
- Existing encrypted data automatically compatible
- Test all encryption/decryption scenarios
From Similar Frameworks
From MyBatis-Plus Encrypt:
- Replace
@Encrypt with @SecurityField
- Configure SM4 key
- Update MyBatis interceptor configuration
From ShardingSphere Encrypt:
- Replace encrypt configuration with annotations
- Migrate encrypted data (may require re-encryption)
- Update application configuration
๐ Real-World Examples
Example 1: User Management System
@Entity
@TableName("sys_user")
public class SysUser {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
@SecurityField
private String password;
@SecurityField
private String idCard;
@SecurityField
private String phone;
@SecurityField
private String email;
private Integer status;
}
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/register")
public void register(@RequestBody SysUser user) {
userService.save(user);
}
@GetMapping("/info/{id}")
@Desensitizationed
public UserVO getUserInfo(@PathVariable Long id) {
SysUser user = userService.getById(id);
return convertToVO(user);
}
}
public class UserVO {
private Long id;
@Masked(type = MaskType.NAME_MASK)
private String name;
@Masked(type = MaskType.PHONE_MASK)
private String phone;
@Masked(type = MaskType.IDCARD_MASK)
private String idCard;
@Masked(type = MaskType.EMAIL_MASK)
private String email;
}
Example 2: Order System with Nested Encryption
@TableName("orders")
public class Order {
@TableId(type = IdType.AUTO)
private Long id;
private String orderNo;
@SecurityField
private String customerPhone;
@SecurityField
private String customerAddress;
@TableField(typeHandler = JacksonTypeHandler.class)
private List<OrderItem> items;
}
public class OrderItem {
private String productName;
@SecurityField
private String receiverPhone;
}
@GetMapping("/order/{id}")
@Desensitizationed
public OrderVO getOrder(@PathVariable Long id) {
Order order = orderService.getById(id);
return convertToVO(order);
}
๐๏ธ Project Structure
rpamis-security/
โโโ rpamis-security-annotation/
โ โโโ @Masked - Masking annotation
โ โโโ @NestedMasked - Nested masking annotation
โ โโโ @SecurityField - Encryption annotation
โ โโโ @Desensitizationed - Controller masking trigger
โ โโโ MaskType - Masking type enum
โโโ rpamis-security-core/
โ โโโ algorithm/ - Encryption algorithms (SM4)
โ โโโ aop/ - Masking AOP aspect
โ โโโ mybatis/ - MyBatis interceptors
โ โโโ field/ - Field processors
โ โโโ factory/ - Mask function factory
โโโ rpamis-security-spring-boot-starter/
โ โโโ SecurityAutoConfiguration
โ โโโ SecurityProperties
โโโ rpamis-security-test/
โโโ 130+ test scenarios (89%+ coverage)
๐ Resources
๐ Key Advantages
| Feature | Rpamis-Security | Similar Projects |
|---|
| Any entity type masking | โ
List, Map, non-generic | โ Single entity only |
| Nested masking | โ
Multi-level nesting | โ Not supported |
| Auto encryption/decryption | โ
Dynamic SQL support | โ Limited functionality |
| SM4 encryption | โ
Supported | Partial support |
| Encryption failure handling | โ
Return original value | โ Not supported |
| Deep copy design | โ
Preserves source reference | โ Not supported |
| Test coverage | โ
89%+ / 130+ scenarios | โ None |
๐ก Pro Tips
- Use
ignore-decrypt-failed: true in development for easier debugging
- Set
ignore-decrypt-failed: false in production to detect data tampering
- Always test with real database to ensure encryption/decryption works correctly
- Use
@Desensitizationed on controller methods not service methods
- Keep encryption keys in environment variables never in source code
- Test nested entity scenarios thoroughly
- Monitor performance for batch operations
- Document which fields are encrypted for team collaboration
๐จ Common Pitfalls
- Forgetting
@Desensitizationed on controller methods
- Wrong key length for SM4 (must be 16 characters)
- Hardcoding encryption keys in source code
- Not testing decryption after encryption
- Ignoring deep copy behavior when updating same object
- Missing AOP configuration in Spring Boot
- Using wrong version for JDK compatibility
Need help? Check the documentation or open an issue.