Rpamis-security Documentation
website·Indexed 30 days ago
https://docs.security.rpamis.comAn enterprise-grade data security component implemented as a MyBatis plugin. It provides functionality for data encryption and decryption, data masking (desensitization) via the @Desensitizationed annotation, and dynamic SQL encryption using the MybatisDynamicSqlEncryptInterceptor.
What's inside Rpamis-security
- Rpamis-security is an enterprise-grade data security component implemented as a MyBatis plugin. It provides annotation-driven data masking and encryption/decryption to solve common pain points such as repetitive encryption logic, inconsistent masking standards, and high maintenance costs. The architecture is divided into three main layers: an Annotation module for marking fields, a Core module for processing logic, and a Spring Boot Auto-configuration module for seamless integration.
Overview of Rpamis-Security
Rpamis-Security is a MyBatis-based security component designed for enterprise data security. It provides annotation-driven data desensitization and automatic database encryption/decryption. Key features include support for 9 built-in desensitization rules, SM4 national standard encryption, support for nested types (List, Map), and a flexible configuration system for failure handling and algorithm selection.Implement automatic data encryption and decryption in MyBatis
Rpamis-security provides a MyBatis plugin that automatically encrypts data when it is inserted or updated in the database and decrypts it when retrieved. The process is transparent to the service layer: the plugin intercepts SQL execution to detect fields marked for security, applies the configured encryption algorithm, and prepends a specific prefix to identify encrypted data. During retrieval, the plugin detects this prefix to trigger decryption.Understand the Rpamis-security data encryption and decryption workflow
Rpamis-security implements data security via MyBatis interceptors.
Encryption Workflow (Data Inbound):
- A Service method calls a MyBatis operation (e.g.,
insert). MybatisEncryptInterceptor.intercepttriggers.- The system performs a deep copy of the source object to prevent modifying original references.
SecurityUtils.encryptencrypts the target fields (checking prefixes to prevent double encryption).- The encrypted parameters are set into the
parameterHandlerand the SQL is executed.
Decryption Workflow (Data Outbound):
- A Service method calls a MyBatis query (e.g.,
select). MybatisDecryptInterceptor.intercepttriggers.- The result set (supporting List, Map, etc.) is processed.
SecurityUtils.decryptdecrypts the fields (checking prefixes to prevent double decryption).- The decrypted objects are returned to the application.
- A Service method calls a MyBatis operation (e.g.,
Install Rpamis-Security Spring Boot Starter
Add therpamis-security-spring-boot-starterdependency to your Maven project. Choose the version based on your JDK version.<!-- For JDK 17 and above --> <dependency> <groupId>com.rpamis</groupId> <artifactId>rpamis-security-spring-boot-starter</artifactId> <version>1.1.4</version> </dependency>
<!-- For JDK 8 to JDK 17 --> <dependency> <groupId>com.rpamis</groupId> <artifactId>rpamis-security-spring-boot-starter</artifactId> <version>1.0.7</version> </dependency>
Handle nested objects for desensitization with @NestedMasked
When a return object contains nested objects, Lists, or Maps that also contain@Maskedfields, use the@NestedMaskedannotation on those fields. This instructs the security component to recursively process the nested structures for desensitization.@Data public class TestNestVO implements Serializable { @Masked(type = MaskType.NAME_MASK) private String name; @NestedMasked private TestVO testVO; @NestedMasked private List<TestVO> testVOList; @NestedMasked private Map<String, TestVO> testVOMap; }Create and use custom desensitization rules
To implement a custom masking logic, create a class that implements theMaskFunctioninterface. Then, apply the@Maskedannotation to the field in your VO, setting the type toMaskType.CUSTOM_MASKand specifying the start/end indices and the masking symbol.// 1. Implement the custom masking logic public class CustomMaskFunction implements MaskFunction { @Override public String mask(String value) { // Example: Replace all digits with asterisks return value.replaceAll("[0-9]", "*"); } } // 2. Apply the custom mask to a VO field @Data public class CardVO implements Serializable { @Masked(type = MaskType.CUSTOM_MASK, start = 0, end = value.length(), symbol = "#") private String cardNumber; }Implement database field encryption and decryption
To automatically encrypt data when saving to the database and decrypt it when reading, add the@SecurityFieldannotation to the fields in your Entity/DO class. This works automatically with MyBatis and MyBatis Plus mappers.@Data @TableName(value ="user") public class UserDO implements Serializable { @TableId(value = "id", type = IdType.AUTO) private Long id; @TableField(value = "name") @SecurityField private String name; @TableField(value = "phone") @SecurityField private String phone; } // Usage in Service @Service public class UserService { @Autowired private UserMapper userMapper; public void saveUser(UserDO user) { userMapper.insert(user); // Automatically encrypts @SecurityField fields } public UserDO getUser(Long id) { return userMapper.selectById(id); // Automatically decrypts @SecurityField fields } }Prevent duplicate encryption of data
The component uses a specific prefix to identify fields that have already been encrypted. This prevents the system from encrypting the same data multiple times, ensuring data consistency even when the same object reference is updated and saved repeatedly.public void testDuplicateEncryptionCheck() { UserInfoDO user = new UserInfoDO(); user.setName("张三"); // First encryption userService.saveUser(user); // Modify the same object reference and update again user.setName("李四"); userService.updateUser(user); // Encrypts normally without duplicating existing encryption }Understand the data desensitization workflow
The data desensitization process follows this flow:
- A Controller method annotated with
@Desensitizationedreturns an object. DesensitizationAspectintercepts the return and callsMaskAnnotationResolver.processObjectAndMask.- The resolver recursively processes the object to handle:
- Field-level masking via
Maskedannotations andMaskFunction. - Collection masking (supporting
ListandMaptypes). - Nested object masking via
NestedMaskedannotations.
- Field-level masking via
- A Controller method annotated with
Implement data desensitization using @Desensitizationed and @Masked
To desensitize data in API responses, use a two-tier annotation approach: first, mark the Controller method with@Desensitizationedto enable the desensitization aspect for that request. Then, mark specific fields in the return object (VO) with@Maskedto define how that field should be masked. The@Maskedannotation supports variousMaskTypevalues (e.g.,NAME_MASK,PHONE_MASK,EMAIL_MASK) and allows custom configuration of start/end positions and masking symbols.// 1. Mark the controller method @PostMapping("/user") @Desensitizationed public UserVO getUser() { return userService.getUser(); } // 2. Mark the fields in the VO @Data public class UserVO implements Serializable { @Masked(type = MaskType.NAME_MASK) private String name; @Masked(type = MaskType.CUSTOM_MASK, start = 2, end = 5, symbol = "#") private String customField; }Implement data desensitization (masking)
To mask sensitive data in API responses, use the@Maskedannotation on the fields of your VO (Value Object) and the@Desensitizationedannotation on the controller method. TheMaskTypedetermines the masking rule (e.g., NAME_MASK, PHONE_MASK).@Data public class UserVO implements Serializable { @Masked(type = MaskType.NAME_MASK) private String name; @Masked(type = MaskType.PHONE_MASK) private String phone; @Masked(type = MaskType.CUSTOM_MASK, start = 2, end = 5, symbol = "#") private String customField; } @RestController @RequestMapping("/api") public class UserController { @PostMapping("/user") @Desensitizationed public UserVO getUser() { // ... return UserVO object } }