# API 文档 📚 API 文档 [#-api-文档] 🎯 注解 API [#-注解-api] 1. 脱敏注解 [#1-脱敏注解] @Desensitizationed [#desensitizationed] **类型**:方法级注解 **用途**:标识需要进行数据脱敏的方法 **示例:** ```java @PostMapping("/user") @Desensitizationed public UserVO getUser() { // 方法逻辑 } ``` **说明**:该注解用于 Controller 层方法上,表示该方法的返回值需要进行脱敏处理。只有标注了该注解的方法,返回值中的 `@Masked` 注解才会生效。 @Masked [#masked] **类型**:字段级注解 **用途**:标识需要进行脱敏处理的字段 **参数:** | 参数 | 类型 | 必填 | 默认值 | 说明 | | -------- | -------- | --- | --- | -------------- | | `type` | MaskType | ✅ 是 | 无 | 脱敏类型(必填) | | `start` | int | ❌ 否 | 0 | 自定义脱敏起始位置(包含) | | `end` | int | ❌ 否 | 0 | 自定义脱敏结束位置(不包含) | | `symbol` | String | ❌ 否 | \* | 脱敏符号 | **示例:** ```java @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; } ``` **脱敏类型(MaskType):** ```java public enum MaskType { /** 不脱敏 */ NO_MASK, /** 姓名脱敏 */ NAME_MASK, /** 电话脱敏 */ PHONE_MASK, /** 身份证脱敏 */ IDCARD_MASK, /** 邮箱脱敏 */ EMAIL_MASK, /** 银行卡脱敏 */ BANKCARD_MASK, /** 地址脱敏 */ ADDRESS_MASK, /** 全脱敏 */ ALL_MASK, /** 自定义脱敏 */ CUSTOM_MASK } ``` @NestedMasked [#nestedmasked] **类型**:字段级注解 **用途**:标识需要进行嵌套脱敏的字段 **示例:** ```java @Data public class TestNestVO implements Serializable { @Masked(type = MaskType.NAME_MASK) private String name; @NestedMasked private TestVO testVO; @NestedMasked private List testVOList; @NestedMasked private Map testVOMap; } ``` **说明**:对于包含脱敏字段的嵌套对象、List、Map 等,需要使用 `@NestedMasked` 注解来标记,组件会递归处理嵌套对象中的 `@Masked` 注解。 2. 加密解密注解 [#2-加密解密注解] @SecurityField [#securityfield] **类型**:字段级注解 **用途**:标识需要进行加解密处理的字段 **示例:** ```java @Data @TableName(value ="user_info") public class UserInfoDO implements Serializable { @TableField(value = "name") @SecurityField private String name; @TableField(value = "id_card") @SecurityField private String idCard; } ``` **说明**:该注解用于实体类字段上,表示该字段在落库时需要加密,出库时需要解密。 ⚙️ 配置 API [#️-配置-api] 1. 全局配置 [#1-全局配置] 配置结构 [#配置结构] ```yaml rpamis: security: # 是否开启安全组件,落库加密,出库脱密 enable: true # 忽略解密失败,如果解密失败则返回原值 ignore-decrypt-failed: true # 是否开启脱敏切面 desensitization-enable: true # 自定义切点,比如增加 RestController 切点 custom-pointcut: '@within(org.springframework.web.bind.annotation.RestController)' # 加解密算法配置 algorithm: # 激活的加密算法 active: sm4 sm4: # 加密算法 key,需要自己生成,满足 16 位即可,下面只是样例 key: 2U43wVWjLgToKBzG # 加解密唯一识别前缀 prefix: Your_CUSTOM_PREFIX_ ``` 配置详解 [#配置详解] **基本配置:** | 配置项 | 类型 | 必填 | 默认值 | 说明 | | ------------------------ | ------- | --- | ---- | ------------------------------ | | `enable` | boolean | ✅ 是 | true | 是否开启安全组件 | | `desensitization-enable` | boolean | ❌ 否 | true | 是否开启脱敏切面 | | `ignore-decrypt-failed` | boolean | ❌ 否 | true | 解密失败时是否忽略,true 返回原值,false 抛出异常 | | `custom-pointcut` | String | ❌ 否 | 无 | 自定义 AOP 切点表达式 | **算法配置:** | 配置项 | 类型 | 必填 | 默认值 | 说明 | | ---------------------- | ------ | --- | --- | ------------------ | | `algorithm.active` | String | ✅ 是 | 无 | 激活的加密算法(目前支持 sm4) | | `algorithm.sm4.key` | String | ✅ 是 | 无 | SM4 算法密钥,必须满足 16 位 | | `algorithm.sm4.prefix` | String | ✅ 是 | 无 | 加密字段前缀,用于识别已加密字段 | **配置类(SecurityProperties):** ```java @Data @ConfigurationProperties(prefix = "rpamis.security") public class SecurityProperties { /** * 是否开启安全组件,落库加密,出库脱密 */ private boolean enable = true; /** * 忽略解密失败,如果解密失败则返回原值,否则抛出异常 */ private boolean ignoreDecryptFailed = true; /** * 是否开启脱敏切面 */ private boolean desensitizationEnable = true; /** * 自定义切点,比如增加 RestController 切点 */ private String customPointcut; /** * 加密算法配置 */ private AlgorithmProperties algorithm = new AlgorithmProperties(); @Data public static class AlgorithmProperties { private String active; private Sm4Properties sm4 = new Sm4Properties(); } @Data public static class Sm4Properties { private String key; private String prefix; } } ``` 2. 算法扩展接口 [#2-算法扩展接口] SecurityAlgorithm [#securityalgorithm] **用途**:加密算法扩展接口,实现该接口可以添加新的加密算法 ```java public interface SecurityAlgorithm { /** * 获取算法类型标识 * * @return 算法类型标识 */ String getAlgorithmType(); /** * 加密操作 * * @param plaintext 明文 * @return 密文 */ String encrypt(String plaintext); /** * 解密操作 * * @param ciphertext 密文 * @return 明文 */ String decrypt(String ciphertext); } ``` **实现示例(SM4 算法):** ```java @Component public class Sm4SecurityAlgorithm implements SecurityAlgorithm { @Override public String getAlgorithmType() { return "sm4"; } @Override public String encrypt(String plaintext) { // 实现 SM4 加密 } @Override public String decrypt(String ciphertext) { // 实现 SM4 解密 } } ``` MaskFunction [#maskfunction] **用途**:脱敏函数接口,实现该接口可以添加新的脱敏规则 ```java @FunctionalInterface public interface MaskFunction { /** * 脱敏方法 * * @param value 原始值 * @return 脱敏后的值 */ String mask(String value); } ``` **实现示例(姓名脱敏):** ```java public class NameMaskFunction implements MaskFunction { @Override public String mask(String value) { if (value.length() == 1) { return value; } return value.charAt(0) + "*".repeat(value.length() - 1); } } ``` 3. 核心组件接口 [#3-核心组件接口] DataMaskingProcessor [#datamaskingprocessor] **用途**:数据脱敏处理器,负责处理字段的脱敏逻辑 ```java public interface DataMaskingProcessor { /** * 处理对象的脱敏 * * @param value 需要处理的对象 * @return 处理后的对象 */ Object process(Object value); } ``` MybatisEncryptInterceptor [#mybatisencryptinterceptor] **用途**:MyBatis 加密拦截器,负责处理数据入库时的加密 ```java @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), }) public class MybatisEncryptInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 加密逻辑 } } ``` MybatisDecryptInterceptor [#mybatisdecryptinterceptor] **用途**:MyBatis 解密拦截器,负责处理数据出库时的解密 ```java @Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}), }) public class MybatisDecryptInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 解密逻辑 } } ``` DesensitizationAspect [#desensitizationaspect] **用途**:脱敏切面,负责拦截带有 `@Desensitizationed` 注解的方法 ```java @Aspect @Component public class DesensitizationAspect { @Pointcut("@annotation(com.rpamis.security.annotation.Desensitizationed)") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { // 脱敏处理逻辑 } } ``` 🧩 扩展接口 [#-扩展接口] 1. 自定义加密算法 [#1-自定义加密算法] ```java @Component public class CustomSecurityAlgorithm implements SecurityAlgorithm { @Override public String getAlgorithmType() { return "custom"; // 自定义算法标识 } @Override public String encrypt(String plaintext) { // 实现自定义加密逻辑 return "encrypted:" + plaintext; } @Override public String decrypt(String ciphertext) { // 实现自定义解密逻辑 return ciphertext.replace("encrypted:", ""); } } ``` **配置使用:** ```yaml rpamis: security: algorithm: active: custom # 使用自定义算法 ``` 2. 自定义脱敏类型 [#2-自定义脱敏类型] ```java // 1. 添加枚举值 public enum MaskType { // 内置类型... CUSTOM_TYPE_MASK } // 2. 实现脱敏函数 public class CustomTypeMaskFunction implements MaskFunction { @Override public String mask(String value) { return value.replaceAll("[0-9]", "*"); // 数字脱敏 } } // 3. 注册脱敏函数 @Configuration public class SecurityConfiguration { @Bean public MaskFunction customTypeMaskFunction() { return new CustomTypeMaskFunction(); } } // 4. 使用 @Data public class TestVO { @Masked(type = MaskType.CUSTOM_TYPE_MASK) private String testField; } ``` 系统属性 [#系统属性] 自动配置类 [#自动配置类] 组件提供了自动配置类,会自动装配所有核心组件: ```java @Configuration @ConditionalOnClass(SqlSessionFactory.class) @EnableConfigurationProperties(SecurityProperties.class) public class MybatisSecurityAutoConfiguration { @Bean @ConditionalOnProperty(prefix = "rpamis.security", name = "enable", havingValue = "true") public MybatisEncryptInterceptor mybatisEncryptInterceptor() { return new MybatisEncryptInterceptor(); } @Bean @ConditionalOnProperty(prefix = "rpamis.security", name = "enable", havingValue = "true") public MybatisDecryptInterceptor mybatisDecryptInterceptor() { return new MybatisDecryptInterceptor(); } @Bean @ConditionalOnProperty(prefix = "rpamis.security", name = "enable", havingValue = "true") public MybatisDynamicSqlEncryptInterceptor mybatisDynamicSqlEncryptInterceptor() { return new MybatisDynamicSqlEncryptInterceptor(); } } @Configuration @ConditionalOnClass(DesensitizationAspect.class) @EnableConfigurationProperties(SecurityProperties.class) @ConditionalOnProperty(prefix = "rpamis.security", name = "desensitization-enable", havingValue = "true", matchIfMissing = true) public class DesensitizationAutoConfiguration { @Bean public DesensitizationAspect desensitizationAspect() { return new DesensitizationAspect(); } } ``` 依赖类 [#依赖类] 1. 工具类 [#1-工具类] RpamisBeanUtil [#rpamisbeanutil] **用途**:Bean 工具类,用于对象的深拷贝和属性拷贝 ```java public class RpamisBeanUtil { /** * 对象深拷贝 * * @param source 源对象 * @param targetClass 目标类 * @param 目标类型 * @return 深拷贝后的对象 */ public static T copy(Object source, Class targetClass) { // 实现深拷贝 } } ``` **使用示例:** ```java TestVO target = RpamisBeanUtil.copy(source, TestVO.class); ``` 2. 异常类 [#2-异常类] SecurityException [#securityexception] **用途**:安全组件的基础异常类 ```java public class SecurityException extends RuntimeException { public SecurityException(String message) { super(message); } public SecurityException(String message, Throwable cause) { super(message, cause); } } ``` 核心接口总结 [#核心接口总结] | 接口/类 | 用途 | 所在包 | | --------------------------- | ------------- | ------------------------------ | | `@Desensitizationed` | 方法级脱敏注解 | com.rpamis.security.annotation | | `@Masked` | 字段级脱敏注解 | com.rpamis.security.annotation | | `@NestedMasked` | 字段级嵌套脱敏注解 | com.rpamis.security.annotation | | `@SecurityField` | 字段级加解密注解 | com.rpamis.security.annotation | | `SecurityAlgorithm` | 加密算法接口 | com.rpamis.security.algorithm | | `DataMaskingProcessor` | 数据脱敏处理器 | com.rpamis.security.mask | | `DesensitizationAspect` | 脱敏切面 | com.rpamis.security.aspect | | `MybatisEncryptInterceptor` | MyBatis 加密拦截器 | com.rpamis.security.mybatis | | `MybatisDecryptInterceptor` | MyBatis 解密拦截器 | com.rpamis.security.mybatis | | `SecurityProperties` | 配置属性类 | com.rpamis.security.properties | # 架构设计 🏗️ 架构设计 [#️-架构设计] Rpamis-security 是基于 MyBatis 插件开发的企业级数据安全组件,旨在提供简单易用的注解式数据脱敏和加密解密功能。 📚 项目背景 [#-项目背景] 🚨 问题分析 [#-问题分析] [Rapmis-Security技术背景](https://benym.cn/notes/08-open-source-project/01-rpamis/03-security/02-rpamis-security-technical-background) 在传统的企业应用开发中,数据安全处理通常存在以下痛点: 1. **繁琐的脱敏操作**:需要在每一个需要脱敏的接口中手动处理 2. **重复的加密逻辑**:相同的加密算法在各个项目中重复实现 3. **缺乏统一标准**:不同项目的脱敏和加密方式不一致 4. **性能开销大**:在高并发场景下,传统的反射和 AOP 方法可能导致性能问题 5. **开发效率低**:安全需求分散在代码各处,维护成本高 💡 解决方案 [#-解决方案] [Rpamis-security-原理解析](https://benym.cn/notes/08-open-source-project/01-rpamis/03-security/03-rpamis-security-principle-analysis) Rpamis-security 提供了以下解决方案: * **注解式编程**:使用注解标记需要处理的字段和方法,简化开发 * **自动装配**:Spring Boot 自动配置,无需复杂的 XML 配置 * **MyBatis 插件**:基于 MyBatis 插件实现数据入库加密和出库解密 * **AOP 切面**:基于 Spring AOP 实现方法级的脱敏控制 * **策略模式**:支持多种加密算法和脱敏规则 * **高性能设计**:优化的反射、缓存和深拷贝策略 总体架构 [#总体架构] ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ 注解模块 │ │ 核心模块 │ │ Spring Boot 自动配置 │ │ SecurityField │ │ SecurityAlgorithm │ │ SecurityAutoConfiguration │ │ Masked │ │ MaskFunction │ │ SecurityProperties │ │ Desensitizationed│ │ SecurityResolver │ │ │ │ NestedMasked │ │ DesensitizationAspect ││ │ └─────────┬───────┘ └─────────┬───────┘ └─────────┬─────────┘ │ │ │ └─────────────────────┼───────────────────────┘ │ ┌────────────────┴─────────────────┐ │ 技术架构 │ └────────────────┬─────────────────┘ │ ┌────────────────┴─────────────────┐ │ 运行时流程 │ └────────────────┬─────────────────┘ │ ┌────────────────┴─────────────────┐ │ 存储层处理(MyBatis) │ │ MybatisEncryptInterceptor │ │ MybatisDecryptInterceptor │ │ MybatisDynamicSqlEncryptInterceptor │ └────────────────┬─────────────────┘ │ ┌────────────────┴─────────────────┐ │ 应用层处理(AOP) │ │ DesensitizationAspect │ │ DesensitizationInterceptor │ └───────────────────────────────────┘ ``` 技术架构 [#技术架构] 1. 核心组件 [#1-核心组件] SecurityResolver(安全解析器) [#securityresolver安全解析器] ```java public class SecurityResolver { // 字段注解缓存 private final ConcurrentHashMap, List> classToFieldMap; // 解密字段缓存 private final ConcurrentHashMap, List> classToDecryptFieldMap; // 安全工具类 private final SecurityUtils securityUtils; // 防重复加密/解密的引用跟踪 private static final Set REFERENCE_SET; private static final Set DECRYPT_REFERENCE_SET; // 字段处理器和类型处理器列表 private static final List PROCESS_LIST; private static final List HANDLER_LIST; } ``` **功能**: * 负责字段级别的加密解密处理 * 提供字段注解的解析和缓存 * 防止字段的重复加密和解密 * 支持多种类型的字段处理(数组、集合、Map、嵌套对象) MaskFunctionFactory(脱敏函数工厂) [#maskfunctionfactory脱敏函数工厂] ```java public class MaskFunctionFactory { private static final Map MASK_FUNCTION_MAP = new ConcurrentHashMap<>(); public MaskFunctionFactory() { MASK_FUNCTION_MAP.put(MaskType.NO_MASK, this::noMask); MASK_FUNCTION_MAP.put(MaskType.NAME_MASK, this::nameMask); MASK_FUNCTION_MAP.put(MaskType.PHONE_MASK, this::phoneMask); MASK_FUNCTION_MAP.put(MaskType.IDCARD_MASK, this::idCardMask); MASK_FUNCTION_MAP.put(MaskType.EMAIL_MASK, this::emailMask); MASK_FUNCTION_MAP.put(MaskType.BANKCARD_MASK, this::bankCardMask); MASK_FUNCTION_MAP.put(MaskType.ADDRESS_MASK, this::addressMask); MASK_FUNCTION_MAP.put(MaskType.ALL_MASK, this::allMask); MASK_FUNCTION_MAP.put(MaskType.CUSTOM_MASK, this::customMask); } } ``` **功能**: * 使用策略模式管理多种脱敏规则 * 提供工厂方法根据脱敏类型获取对应的脱敏函数 * 支持自定义脱敏类型的扩展 类型处理器(TypeHandler) [#类型处理器typehandler] ```java // 数组类型处理器 public class ArrayTypeHandler implements TypeHandler { public void handleType(ProcessContext context) { ... } } // 集合类型处理器 public class CollectionTypeHandler implements TypeHandler { public void handleType(ProcessContext context) { ... } } // Map 类型处理器 public class MapTypeHandler implements TypeHandler { public void handleType(ProcessContext context) { ... } } // 其他类型处理器 public class OtherTypeHandler implements TypeHandler { public void handleType(ProcessContext context) { ... } } ``` **功能**: * 处理不同类型的字段值 * 提供统一的类型处理接口 * 支持嵌套类型的递归处理 2. MyBatis 插件组件 [#2-mybatis-插件组件] MybatisEncryptInterceptor(加密拦截器) [#mybatisencryptinterceptor加密拦截器] ```java @Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class) }) public class MybatisEncryptInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 拦截 ParameterHandler 的 setParameters 方法 // 在 SQL 执行前对参数进行加密处理 // 支持 MyBatis Plus 的特殊参数格式 // 对对象进行深拷贝,防止修改源对象引用 return invocation.proceed(); } } ``` **特点**: * 在 ParameterHandler 级别拦截 * 支持 MyBatis 和 MyBatis Plus * 使用深拷贝防止源对象被修改 * 忽略 select 操作(只处理 insert/update) MybatisDecryptInterceptor(解密拦截器) [#mybatisdecryptinterceptor解密拦截器] ```java @Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = Statement.class) }) public class MybatisDecryptInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object result = invocation.proceed(); // 对查询结果进行解密处理 return securityResolver.decryptFiled(result); } } ``` **特点**: * 在 ResultSetHandler 级别拦截 * 自动解密查询结果 * 支持各种类型的返回值(单一对象、List、Map 等) MybatisDynamicSqlEncryptInterceptor(动态 SQL 加密拦截器) [#mybatisdynamicsqlencryptinterceptor动态-sql-加密拦截器] ```java @Intercepts({ @Signature(type = Executor.class, method = "update", args = MappedStatement.class, Object.class) }) public class MybatisDynamicSqlEncryptInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 处理使用 foreach 标签的动态 SQL 场景 // 对参数进行加密处理 } } ``` **特点**: * 专门处理动态 SQL 场景 * 支持 foreach 标签的参数加密 * 在 Executor 级别拦截 3. AOP 组件 [#3-aop-组件] DesensitizationAspect(脱敏切面) [#desensitizationaspect脱敏切面] ```java public class DesensitizationAspect { public Object around(ProceedingJoinPoint joinPoint) throws Throwable { Object[] args = joinPoint.getArgs(); Object result = joinPoint.proceed(args); try { result = MaskAnnotationResolver.processObjectAndMask(result); } catch (Exception e) { LOGGER.error("DesensitizationAspect process unknown error", e); } return result; } } ``` **功能**: * 拦截带有 `@Desensitizationed` 注解的方法 * 对返回值进行脱敏处理 * 容错处理,不影响业务逻辑 DesensitizationAdvisor(脱敏顾问) [#desensitizationadvisor脱敏顾问] ```java public class DesensitizationAdvisor implements Advisor { // 配置 AOP 切面和拦截器 } ``` **功能**: * 配置 AOP 的 pointcut 和 advice * 支持自定义切点表达式 4. 加密算法组件 [#4-加密算法组件] SecurityAlgorithm(加密算法接口) [#securityalgorithm加密算法接口] ```java public interface SecurityAlgorithm { String encrypt(String sourceValue); String decrypt(String sourceValue); } ``` Sm4SecurityAlgorithmImpl(SM4 算法实现) [#sm4securityalgorithmimplsm4-算法实现] ```java @Component public class Sm4SecurityAlgorithmImpl implements SecurityAlgorithm { private final String key; private final String prefix; public Sm4SecurityAlgorithmImpl(SecurityConfigProvider configProvider) { this.key = configProvider.getAlgorithmConfig().getSm4().getKey(); this.prefix = configProvider.getAlgorithmConfig().getSm4().getPrefix(); } @Override public String encrypt(String sourceValue) { // SM4 加密逻辑 return prefix + encryptedValue; } @Override public String decrypt(String sourceValue) { // SM4 解密逻辑 return decryptedValue; } } ``` **支持的算法**: * **SM4**(国密算法):128位对称加密算法,支持自定义密钥和前缀 * **可扩展**:实现 SecurityAlgorithm 接口添加新算法 5. 配置组件 [#5-配置组件] SecurityProperties(配置属性类) [#securityproperties配置属性类] ```java @Data @ConfigurationProperties(prefix = "rpamis.security") public class SecurityProperties { private boolean enable = true; private boolean desensitizationEnable = true; private boolean ignoreDecryptFailed = true; private String customPointcut; private Algorithm algorithm; @Data public static class Algorithm { private String active; private Sm4Config sm4; } @Data public static class Sm4Config { private String key; private String prefix = DefaultPrefix.DEFAULT_PREFIX; } } ``` SecurityAutoConfiguration(自动配置类) [#securityautoconfiguration自动配置类] ```java @Configuration @ConditionalOnClass(SqlSessionFactory.class) @EnableConfigurationProperties(SecurityProperties.class) public class SecurityAutoConfiguration { @Bean @ConditionalOnProperty(prefix = "rpamis.security", name = "enable", havingValue = "true") public MybatisEncryptInterceptor mybatisEncryptInterceptor() { return new MybatisEncryptInterceptor(securityResolver()); } @Bean @ConditionalOnProperty(prefix = "rpamis.security", name = "enable", havingValue = "true") public MybatisDecryptInterceptor mybatisDecryptInterceptor() { return new MybatisDecryptInterceptor(securityResolver()); } @Bean @ConditionalOnProperty(prefix = "rpamis.security", name = "desensitization-enable", havingValue = "true") public DesensitizationAspect desensitizationAspect() { return new DesensitizationAspect(); } } ``` 工作原理分析 [#工作原理分析] 1. 数据脱敏流程 [#1-数据脱敏流程] ``` ┌─────────────────────────────────────────────────────────────────┐ │ 请求处理流程 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────┐ ┌──────────────────────┐ │ Controller 方法 │ │ DesensitizationAspect│ │ @Desensitizationed │───────▶│ 拦截请求,调用 MaskAnnotationResolver │ │ 返回需要脱敏的对象 │ └──────────────────────┘ └──────────────────────┘ │ ▼ ┌──────────────────────────────────┐ │ MaskAnnotationResolver.processObjectAndMask │ └──────────────────────────────────┘ │ ┌──────────────────────┬──────────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 处理字段脱敏 │ │ 处理集合脱敏 │ │ 处理嵌套对象 │ │ Masked 注解 │ │ 支持 List 类型│ │ NestedMasked │ │ MaskFunction │ │ 支持 Map 类型 │ │ 递归处理 │ └──────────────┘ └──────────────┘ └──────────────┘ ``` 2. 数据加密流程 [#2-数据加密流程] ``` ┌─────────────────────────────────────────────────────────────────┐ │ 数据入库流程 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────┐ ┌──────────────────────┐ │ Service 方法 │ │ MyBatis 操作 │ │ save(user) │───────▶│ insert into user... │ └──────────────────────┘ └──────────────────────┘ │ ▼ ┌──────────────────────────────────┐ │ MybatisEncryptInterceptor.intercept │ └──────────────────────────────────┘ │ ┌──────────────────────┬──────────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 深拷贝源对象 │ │ 加密字段值 │ │ 修改参数 │ │ 防止修改源引用│ │ SecurityUtils.encrypt │ 设置到 parameterHandler │ │ SerializationUtils │ │ 支持前缀检查防止重复加密 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ ▼ ┌──────────────────────────────────┐ │ 执行加密后的 SQL 语句 │ └──────────────────────────────────┘ ``` 3. 数据解密流程 [#3-数据解密流程] ``` ┌─────────────────────────────────────────────────────────────────┐ │ 数据出库流程 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────┐ ┌──────────────────────┐ │ Service 方法 │ │ MyBatis 查询 │ │ getUser(id) │───────▶│ select * from user...│ └──────────────────────┘ └──────────────────────┘ │ ▼ ┌──────────────────────────────────┐ │ MybatisDecryptInterceptor.intercept │ └──────────────────────────────────┘ │ ┌──────────────────────┬──────────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 处理结果集 │ │ 解密字段值 │ │ 返回给应用程序 │ │ 支持各种返回类型 │ │ SecurityUtils.decrypt │ 用户获取解密后对象 │ │ List、Map 等 │ │ 支持前缀检查防止重复解密 │ │ └──────────────┘ └──────────────┘ └──────────────┘ ``` 高性能优化策略 [#高性能优化策略] 1. 缓存策略 [#1-缓存策略] ```java // 字段注解缓存 private final ConcurrentHashMap, List> classToFieldMap; // 解密字段缓存 private final ConcurrentHashMap, List> classToDecryptFieldMap; // 防重复加密/解密的引用跟踪 private static final Set REFERENCE_SET = Collections.newSetFromMap(new IdentityHashMap<>(128)); ``` **优化说明**: * 缓存类的字段信息,避免重复解析 * 使用 ConcurrentHashMap 提高并发访问性能 * 防止重复加密/解密,避免性能损耗 2. 深拷贝策略 [#2-深拷贝策略] ```java public static T deepClone(T source) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(source); oos.close(); ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream(baos.toByteArray()) ); @SuppressWarnings("unchecked") T clone = (T) ois.readObject(); ois.close(); return clone; } catch (Exception e) { throw new SecurityException(e); } } ``` **优化说明**: * 使用序列化实现深拷贝 * 避免修改源对象的引用 * 支持循环引用和复杂对象结构 3. 防重复处理 [#3-防重复处理] ```java // 防重复加密 private static final Set REFERENCE_SET = Collections.newSetFromMap(new IdentityHashMap<>(128)); // 防重复解密 private static final Set DECRYPT_REFERENCE_SET = Collections.newSetFromMap(new IdentityHashMap<>(128)); ``` **优化说明**: * 使用 IdentityHashMap 跟踪对象引用 * 同一对象在处理过程中只被处理一次 * 防止循环引用导致的栈溢出 4. 字段处理优化 [#4-字段处理优化] ```java // 字段处理者列表(链式处理) private static final List PROCESS_LIST = new ArrayList<>(); // 类型处理者列表(策略模式) private static final List HANDLER_LIST = new ArrayList<>(); // 初始化 PROCESS_LIST.add(new FinderSecurityProcessor()); PROCESS_LIST.add(new SecurityAnnotationProcessor(securityUtils)); HANDLER_LIST.add(new ArrayTypeHandler()); HANDLER_LIST.add(new CollectionTypeHandler()); HANDLER_LIST.add(new MapTypeHandler()); HANDLER_LIST.add(new OtherTypeHandler()); ``` **优化说明**: * 使用责任链模式处理字段 * 使用策略模式处理不同类型 * 高性能的字段处理算法 配置与扩展性 [#配置与扩展性] 1. 加密算法扩展 [#1-加密算法扩展] ```java @Component public class CustomSecurityAlgorithm implements SecurityAlgorithm { @Override public String encrypt(String sourceValue) { // 自定义加密逻辑 return "custom:" + sourceValue; } @Override public String decrypt(String sourceValue) { // 自定义解密逻辑 return sourceValue.replace("custom:", ""); } } ``` ```yaml rpamis: security: algorithm: active: custom # 使用自定义算法 custom: key: your-custom-key prefix: CUSTOM_ ``` 2. 脱敏规则扩展 [#2-脱敏规则扩展] ```java // 1. 添加枚举值 public enum MaskType { CUSTOM_TYPE_MASK } // 2. 实现脱敏函数 public class CustomTypeMaskFunction implements MaskFunction { @Override public String mask(String value) { return value.replaceAll("[0-9]", "*"); // 数字脱敏 } } // 3. 注册到工厂 @Configuration public class SecurityConfiguration { @Bean public MaskFunction customTypeMaskFunction() { return new CustomTypeMaskFunction(); } } // 4. 使用 @Data public class TestVO { @Masked(type = MaskType.CUSTOM_TYPE_MASK) private String testField; } ``` 3. 自定义切点 [#3-自定义切点] ```yaml rpamis: security: custom-pointcut: '@within(org.springframework.web.bind.annotation.RestController)' ``` **用途**: * 自定义 AOP 切点表达式 * 更精确地控制哪些方法需要脱敏 * 可以与其他 AOP 框架集成 # 数据脱敏 import { Cards, Card } from '@/components/card'; import { Callout } from '@/components/callout'; import { Tabs, TabList, TabTrigger, TabContent } from '@/components/tabs'; 🎭 数据脱敏 [#-数据脱敏] 数据脱敏是 Rpamis-security 的核心功能之一,通过注解式编程方式,开发者只需要对需要处理的字段或方法加上对应注解,无需关心脱敏的具体实现,由组件自动完成。 📋 内置脱敏规则 [#-内置脱敏规则] 组件内置了 **9种** 脱敏规则: | 规则 | 说明 | 示例 | | --------------- | ----- | ------------------------------------------------------------------------------ | | `NO_MASK` | 不脱敏 | 原始值 | | `NAME_MASK` | 姓名脱敏 | 张三 → 张\* | | `PHONE_MASK` | 电话脱敏 | 13812345678 → 138\*\*\*\*5678 | | `IDCARD_MASK` | 身份证脱敏 | 110101199003073328 → 110\*\*\*\*\*\*\*\*\*\*\*3328 | | `EMAIL_MASK` | 邮箱脱敏 | [zhangsan@example.com](mailto:zhangsan@example.com) → zhan\*\*\*\*@example.com | | `BANKCARD_MASK` | 银行卡脱敏 | 6222020200099998888 → 6222 \*\*\*\* \*\*\*\* 8888 | | `ADDRESS_MASK` | 地址脱敏 | 北京市朝阳区XX街道 → 北京市朝阳区\*\*\*\*\*\*\*\* | | `ALL_MASK` | 全脱敏 | 所有字符替换为 \* | | `CUSTOM_MASK` | 自定义脱敏 | 可指定起始和结束位置 | 所有脱敏规则都支持自定义脱敏标识符,默认为 \*。 🔧 使用方法 [#-使用方法] 1. 单一实体脱敏 [#1-单一实体脱敏] 在需要脱敏的字段上使用 `@Masked` 注解: ```java @Data public class TestVO implements Serializable { private static final long serialVersionUID = 1142843493987112387L; /** * 主键 id,不脱敏 */ private Long id; /** * 姓名 - 使用姓名脱敏规则 */ @Masked(type = MaskType.NAME_MASK) private String name; /** * 身份证号 - 使用身份证脱敏规则 */ @Masked(type = MaskType.IDCARD_MASK) private String idCard; /** * 手机号 - 使用手机号脱敏规则 */ @Masked(type = MaskType.PHONE_MASK) private String phone; /** * 邮箱 - 使用邮箱脱敏规则 */ @Masked(type = MaskType.EMAIL_MASK) private String email; /** * 银行卡号 - 使用银行卡脱敏规则 */ @Masked(type = MaskType.BANKCARD_MASK) private String bankCard; /** * 地址 - 使用地址脱敏规则 */ @Masked(type = MaskType.ADDRESS_MASK) private String address; /** * 自定义标识字段 - 自定义脱敏 * 从第 2 位开始脱敏到第 5 位,使用 # 作为脱敏符号 */ @Masked(type = MaskType.CUSTOM_MASK, start = 2, end = 5, symbol = "#") private String customFiled; } ``` 在 Controller 方法上使用 `@Desensitizationed` 注解来开启脱敏: ```java @RestController @RequestMapping("/api") public class TestController { /** * 获取脱敏数据 - 单一实体 * * @return TestVO */ @PostMapping("/single") @Desensitizationed public TestVO testSingle() { TestVO vo = new TestVO(); vo.setId(1L); vo.setName("张三"); vo.setIdCard("110101199003073328"); vo.setPhone("13812345678"); vo.setEmail("zhangsan@example.com"); vo.setBankCard("6222020200099998888"); vo.setAddress("北京市朝阳区建国路88号"); vo.setCustomFiled("1234567890"); return vo; } } ``` 2. List 类型脱敏 [#2-list-类型脱敏] List 类型同样支持脱敏: ```java @RestController @RequestMapping("/api") public class TestController { /** * 获取脱敏数据 - List 类型 * * @return List */ @PostMapping("/list") @Desensitizationed public List testList() { List list = new ArrayList<>(); for (int i = 0; i < 3; i++) { TestVO vo = new TestVO(); vo.setId((long) i); vo.setName("张三" + i); vo.setIdCard("11010119900307332" + i); vo.setPhone("1381234567" + i); list.add(vo); } return list; } } ``` 3. Map 类型脱敏 [#3-map-类型脱敏] Map 类型也支持脱敏: ```java @RestController @RequestMapping("/api") public class TestController { /** * 获取脱敏数据 - Map 类型 * * @return Map */ @PostMapping("/map") @Desensitizationed public Map testMap() { Map map = new HashMap<>(); TestVO vo1 = new TestVO(); vo1.setId(1L); vo1.setName("张三"); vo1.setIdCard("110101199003073328"); vo1.setPhone("13812345678"); TestVO vo2 = new TestVO(); vo2.setId(2L); vo2.setName("李四"); vo2.setIdCard("110101199003073329"); vo2.setPhone("13812345679"); map.put("user1", vo1); map.put("user2", vo2); return map; } } ``` 4. 统一返回体脱敏 [#4-统一返回体脱敏] 支持统一返回体类型,无论是否有泛型: ```java @Data public class R implements Serializable { private Integer code; private String message; private T data; private long timestamp; } @RestController @RequestMapping("/api") public class TestController { /** * 获取脱敏数据 - 统一返回体(泛型) * * @return R */ @PostMapping("/result/generic") @Desensitizationed public R testGenericResult() { TestVO vo = new TestVO(); vo.setId(1L); vo.setName("张三"); vo.setIdCard("110101199003073328"); vo.setPhone("13812345678"); return R.ok(vo); } /** * 获取脱敏数据 - 统一返回体(无泛型) * * @return R */ @PostMapping("/result/raw") @Desensitizationed public R testRawResult() { TestVO vo = new TestVO(); vo.setId(1L); vo.setName("张三"); vo.setIdCard("110101199003073328"); vo.setPhone("13812345678"); return R.ok(vo); } } ``` 🎨 自定义脱敏 [#-自定义脱敏] 使用 `CUSTOM_MASK` 类型可以实现自定义脱敏: ```java @Data public class CustomTestVO implements Serializable { /** * 自定义脱敏 - 从第 1 位开始到第 8 位,使用 * */ @Masked(type = MaskType.CUSTOM_MASK, start = 1, end = 8) private String data1; /** * 自定义脱敏 - 从第 3 位开始到第 6 位,使用 # */ @Masked(type = MaskType.CUSTOM_MASK, start = 3, end = 6, symbol = "#") private String data2; } ``` **参数说明:** | 参数 | 说明 | 类型 | 默认值 | | -------- | --------- | -------- | --- | | `type` | 脱敏类型 | MaskType | 必填 | | `start` | 起始位置(包含) | int | 0 | | `end` | 结束位置(不包含) | int | 0 | | `symbol` | 脱敏符号 | String | \* | 🔄 脱敏原理 [#-脱敏原理] Rpamis-Security 的脱敏功能基于 **AOP 切面** 实现,通过拦截带有 @Desensitizationed 注解的方法,对其返回值进行脱敏处理。 ⚠️ 注意事项 [#️-注意事项] 1. @Desensitizationed 注解是必需的 [#1-desensitizationed-注解是必需的] 即使字段上有 `@Masked` 注解,如果 Controller 方法上没有 `@Desensitizationed` 注解,脱敏也不会生效。 2. 不依赖 Jackson 序列化 [#2-不依赖-jackson-序列化] Rpamis-security 的脱敏功能不依赖 Jackson 序列化,因此不会影响全局 Jackson 的行为。 3. 支持任意类型返回值 [#3-支持任意类型返回值] 无论是单一实体、List、Map、统一返回体,还是嵌套类型,都能正确处理。 4. 脱敏的范围控制 [#4-脱敏的范围控制] 通过 `@Desensitizationed` 注解实现方法级的脱敏控制,可以精确控制哪些接口返回脱敏数据。 5. 空值处理 [#5-空值处理] 如果字段值为 null,脱敏处理器会直接返回 null,不会抛出异常。 6. 非字符串类型 [#6-非字符串类型] 脱敏功能主要针对字符串类型字段,非字符串类型字段会被忽略。 📊 性能优化 [#-性能优化] 组件优化了脱敏算法,确保在高并发场景下的性能。 脱敏过程是单向的,无法从脱敏后的值恢复原始数据。 Rpamis-Security 提供了强大而灵活的数据脱敏功能,让开发者能够快速集成数据安全保护,同时保持代码的简洁性和可读性。 # 数据加密解密 import { Cards, Card } from '@/components/card'; import { Callout } from '@/components/callout'; import { Steps, Step } from '@/components/steps'; 🔐 数据加密解密 [#-数据加密解密] Rpamis-security 提供了基于 MyBatis 插件的自动数据加密解密功能,支持在数据入库时自动加密,出库时自动解密,无需手动处理。 🔄 工作原理 [#-工作原理] 加密过程 [#加密过程] 当数据通过 MyBatis 或 MyBatis Plus 插入或更新到数据库时 MyBatis 插件拦截 SQL 执行过程,检测到带有 @SecurityField 注解的字段 对字段值进行加密(根据配置的算法),添加加密前缀标识已加密字段 将加密后的值存入数据库 解密过程 [#解密过程] 当通过 MyBatis 或 MyBatis Plus 查询数据时 MyBatis 插件拦截结果集返回过程,检测到带有加密前缀的字段 对字段值进行解密(根据配置的算法) 返回解密后的原始值 💻 使用方法 [#-使用方法] 1. 字段注解 [#1-字段注解] 在需要加密的字段上添加 `@SecurityField` 注解: ```java @Data @TableName(value ="user_info") public class UserInfoDO implements Serializable { private static final long serialVersionUID = 1L; @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; /** * 邮箱 - 需要加密 */ @TableField(value = "email") @SecurityField private String email; /** * 地址 - 需要加密 */ @TableField(value = "address") @SecurityField private String address; /** * 创建时间 - 不需要加密 */ @TableField(value = "create_time") private LocalDateTime createTime; } ``` 2. MyBatis Plus 使用 [#2-mybatis-plus-使用] ```java @Service public class UserInfoService { @Autowired private UserInfoMapper userInfoMapper; /** * 新增用户 - 自动加密 */ public UserInfoDO saveUser(UserInfoDO user) { user.setCreateTime(LocalDateTime.now()); userInfoMapper.insert(user); return user; } /** * 更新用户 - 自动加密 */ public boolean updateUser(UserInfoDO user) { return userInfoMapper.updateById(user) > 0; } /** * 查询用户 - 自动解密 */ public UserInfoDO getUser(Long id) { return userInfoMapper.selectById(id); } /** * 查询用户列表 - 自动解密 */ public List getUsers(String name) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.like("name", name); return userInfoMapper.selectList(queryWrapper); } /** * 删除用户 - 自动处理 */ public boolean deleteUser(Long id) { return userInfoMapper.deleteById(id) > 0; } } ``` 3. MyBatis 自定义 SQL 使用 [#3-mybatis-自定义-sql-使用] 对于自定义 SQL 语句,组件同样支持自动加密解密: ```xml INSERT INTO user_info (id, name, id_card, phone, email, address, create_time) VALUES (#{id}, #{name}, #{idCard}, #{phone}, #{email}, #{address}, #{createTime}) UPDATE user_info SET name = #{name}, id_card = #{idCard}, phone = #{phone}, email = #{email}, address = #{address} WHERE id = #{id} DELETE FROM user_info WHERE id = #{id} ``` ```java public interface UserInfoMapper extends BaseMapper { int insertCustom(UserInfoDO user); int updateCustom(UserInfoDO user); List selectByCustomCondition(@Param("name") String name, @Param("idCard") String idCard); int deleteCustom(Long id); } ``` 4. 动态 SQL 支持 [#4-动态-sql-支持] 对于使用 `` 标签的动态 SQL,组件也提供支持: ```xml INSERT INTO user_info (name, id_card, phone, email, address, create_time) VALUES (#{item.name}, #{item.idCard}, #{item.phone}, #{item.email}, #{item.address}, #{item.createTime}) ``` ```java public interface UserInfoMapper extends BaseMapper { int batchInsert(List users); } ``` 🔐 加密算法 [#-加密算法] 支持的算法 [#支持的算法] 目前组件支持以下加密算法: | 算法 | 说明 | 密钥长度 | 类型 | | --- | --------- | ----- | ---- | | SM4 | 国密 SM4 算法 | 16 字节 | 对称加密 | 配置示例 [#配置示例] ```yaml rpamis: security: enable: true algorithm: active: sm4 sm4: # 16 位密钥,需要自定义 key: 2U43wVWjLgToKBzG # 加密前缀,用于标识已加密字段 prefix: RPAMIS_SECURE_ ``` 算法扩展性 [#算法扩展性] 组件支持自定义加密算法,只需要实现 `SecurityAlgorithm` 接口: ```java public interface SecurityAlgorithm { /** * 获取算法类型 * @return 算法类型名称 */ String getAlgorithmType(); /** * 加密 * @param plaintext 明文 * @return 密文 */ String encrypt(String plaintext); /** * 解密 * @param ciphertext 密文 * @return 明文 */ String decrypt(String ciphertext); } ``` ✨ 重要特性 [#-重要特性] 1. 防止重复加密 [#1-防止重复加密] 组件会检查字段值是否已包含加密前缀,如果已包含则不进行重复加密。 ```java public void testPreventDuplicateEncryption() { UserInfoDO user = new UserInfoDO(); user.setName("张三"); // 第一次加密 String encrypted = encryptionService.encrypt(user.getName()); // encrypted = RPAMIS_SECURE_8A7B6C5D4E3F2A1B... // 第二次加密 - 识别到前缀,直接返回 String duplicateEncrypted = encryptionService.encrypt(encrypted); // duplicateEncrypted = RPAMIS_SECURE_8A7B6C5D4E3F2A1B... } ``` 2. 存量数据兼容 [#2-存量数据兼容] 如果数据库中存在未加密的数据,查询时会检查是否包含加密前缀。如果没有前缀,说明是旧数据,组件会直接返回原始值。 ```java public void testLegacyDataCompatibility() { // 数据库中存在未加密的旧数据 String legacyData = "未加密的旧数据"; // 查询时不会解密,直接返回 String decrypted = decryptionService.decrypt(legacyData); // decrypted = "未加密的旧数据" } ``` 3. 解密失败处理 [#3-解密失败处理] 配置文件中可以设置解密失败时的行为: ```yaml rpamis: security: ignore-decrypt-failed: true # 默认 true ``` * `true`:解密失败时返回原始值 * `false`:解密失败时抛出异常 4. 深拷贝设计 [#4-深拷贝设计] 为了避免影响原对象引用,组件使用深拷贝技术处理加解密。 ```java public void testDeepCopy() { UserInfoDO user = new UserInfoDO(); user.setName("张三"); user.setIdCard("110101199003073328"); // 保存到数据库 - 内部进行深拷贝 userService.saveUser(user); // user 对象的引用没有改变,值仍然是原始值 System.out.println(user.getName()); // 张三(不是加密后的值) } ``` 5. 重复加密检查 [#5-重复加密检查] 组件通过前缀标识避免重复加密,确保数据一致性。 ```java public void testDuplicateEncryptionCheck() { UserInfoDO user = new UserInfoDO(); user.setName("张三"); // 第一次加密 userService.saveUser(user); // 修改同一对象引用后再次更新 user.setName("李四"); userService.updateUser(user); // 正常加密 } ``` 🚀 性能优化 [#-性能优化] ⚙️ 配置说明 [#️-配置说明] MyBatis 插件配置 [#mybatis-插件配置] 组件会自动配置 MyBatis 插件,无需手动配置: ```java @Configuration @ConditionalOnClass(SqlSessionFactory.class) @EnableConfigurationProperties(SecurityProperties.class) public class MybatisSecurityAutoConfiguration { @Bean @ConditionalOnProperty(prefix = "rpamis.security", name = "enable", havingValue = "true") public MybatisEncryptInterceptor mybatisEncryptInterceptor() { return new MybatisEncryptInterceptor(); } @Bean @ConditionalOnProperty(prefix = "rpamis.security", name = "enable", havingValue = "true") public MybatisDecryptInterceptor mybatisDecryptInterceptor() { return new MybatisDecryptInterceptor(); } @Bean @ConditionalOnProperty(prefix = "rpamis.security", name = "enable", havingValue = "true") public MybatisDynamicSqlEncryptInterceptor mybatisDynamicSqlEncryptInterceptor() { return new MybatisDynamicSqlEncryptInterceptor(); } } ``` 字段识别原理 [#字段识别原理] 组件通过以下方式识别需要加解密的字段: 扫描实体类中的所有字段 检查是否有 @SecurityField 注解 检查字段类型是否为 String 支持继承字段的扫描 ⚠️ 注意事项 [#️-注意事项] 1. 字段长度调整 [#1-字段长度调整] 加密后的字段长度会增加,需要在数据库设计时预留足够的空间。 2. 索引问题 [#2-索引问题] 加密后的字段无法直接用于范围查询或索引优化。 3. 查询条件加密 [#3-查询条件加密] 如果需要通过加密字段作为查询条件,必须确保传入的查询值已经过加密处理。 4. 事务一致性 [#4-事务一致性] 加解密操作不会影响事务的一致性。 5. 异常处理 [#5-异常处理] 配置 `ignore-decrypt-failed` 为 true 可以防止解密失败导致整个查询失败。 Rpamis-Security 提供了企业级的数据加密解密功能,通过 MyBatis 插件实现自动化处理,让开发者能够轻松实现数据安全保护! # 使用示例 🎯 使用示例 [#-使用示例] 🏁 基础使用示例 [#-基础使用示例] 1. 单一实体脱敏 [#1-单一实体脱敏] ```java @Data public class UserVO implements Serializable { private static final long serialVersionUID = 1L; @Masked(type = MaskType.NAME_MASK) private String name; @Masked(type = MaskType.IDCARD_MASK) private String idCard; @Masked(type = MaskType.PHONE_MASK) private String phone; @Masked(type = MaskType.EMAIL_MASK) private String email; @Masked(type = MaskType.BANKCARD_MASK) private String bankCard; } @RestController @RequestMapping("/api") public class UserController { @PostMapping("/user") @Desensitizationed public UserVO getUser() { UserVO user = new UserVO(); user.setName("张三"); user.setIdCard("110101199003073328"); user.setPhone("13812345678"); user.setEmail("zhangsan@example.com"); user.setBankCard("6222020200099998888"); return user; } } ``` **返回结果:** ```json { "name": "张*", "idCard": "110***********3328", "phone": "138****5678", "email": "zhan****@example.com", "bankCard": "6222 **** **** 8888" } ``` 2. List 类型脱敏 [#2-list-类型脱敏] ```java @Data public class ProductVO implements Serializable { private static final long serialVersionUID = 1L; private Long id; @Masked(type = MaskType.ALL_MASK) private String productCode; @Masked(type = MaskType.ADDRESS_MASK) private String warehouseLocation; } @RestController @RequestMapping("/api") public class ProductController { @PostMapping("/products") @Desensitizationed public List getProducts() { List products = new ArrayList<>(); ProductVO product1 = new ProductVO(); product1.setId(1L); product1.setProductCode("PRD202401001"); product1.setWarehouseLocation("上海市浦东新区张江高科技园区"); products.add(product1); ProductVO product2 = new ProductVO(); product2.setId(2L); product2.setProductCode("PRD202401002"); product2.setWarehouseLocation("北京市朝阳区建国路88号"); products.add(product2); return products; } } ``` **返回结果:** ```json [ { "id": 1, "productCode": "***********", "warehouseLocation": "上海市浦东新区**********" }, { "id": 2, "productCode": "***********", "warehouseLocation": "北京市朝阳区********" } ] ``` 3. Map 类型脱敏 [#3-map-类型脱敏] ```java @Data public class OrderVO implements Serializable { private static final long serialVersionUID = 1L; private String orderId; @Masked(type = MaskType.PHONE_MASK) private String customerPhone; @Masked(type = MaskType.ADDRESS_MASK) private String shippingAddress; } @RestController @RequestMapping("/api") public class OrderController { @PostMapping("/orders") @Desensitizationed public Map getOrders() { Map orders = new HashMap<>(); OrderVO order1 = new OrderVO(); order1.setOrderId("ORD202401001"); order1.setCustomerPhone("13912345678"); order1.setShippingAddress("广州市天河区珠江新城"); orders.put("order1", order1); OrderVO order2 = new OrderVO(); order2.setOrderId("ORD202401002"); order2.setCustomerPhone("13612345678"); order2.setShippingAddress("深圳市南山区科技园"); orders.put("order2", order2); return orders; } } ``` **返回结果:** ```json { "order1": { "orderId": "ORD202401001", "customerPhone": "139****5678", "shippingAddress": "广州市天河区****" }, "order2": { "orderId": "ORD202401002", "customerPhone": "136****5678", "shippingAddress": "深圳市南山区****" } } ``` 📦 嵌套类型示例 [#-嵌套类型示例] 1. 嵌套实体脱敏 [#1-嵌套实体脱敏] 采用`@NestedMasked`标记嵌套脱敏实体,如`UserDetailVO`中包含`ContactInfoVO`实体,`ContactInfoVO`中包含`@Masked`标记需要脱敏字段 ```java @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; } @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; } } ``` **返回结果:** ```json { "name": "张*", "contactInfo": { "phone": "138****5678", "email": "zhan****@example.com", "address": { "homeAddress": "北京市朝阳区********", "officeAddress": "北京市海淀区****" } } } ``` 🔐 加密解密示例 [#-加密解密示例] 1. MyBatis Plus 基础使用 [#1-mybatis-plus-基础使用] ```java @Data @TableName(value ="customer") public class CustomerDO implements Serializable { private static final long serialVersionUID = 1L; @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; @TableField(value = "email") @SecurityField private String email; } @Mapper public interface CustomerMapper extends BaseMapper { // 继承 BaseMapper 获得基本的 CRUD 方法 } @Service public class CustomerService { @Autowired private CustomerMapper customerMapper; public void saveCustomer() { CustomerDO customer = new CustomerDO(); customer.setName("李四"); customer.setIdCard("110101199003073329"); customer.setPhone("13912345678"); customer.setEmail("lisi@example.com"); customerMapper.insert(customer); } public CustomerDO getCustomer(Long id) { return customerMapper.selectById(id); } } ``` **数据库存储(加密后):** ```sql -- 使用默认前缀和密钥的效果示例 -- 实际加密值会因密钥不同而变化 id | name | id_card | phone | email ---|------|---------|-------|------- 1 | Your_CUSTOM_PREFIX_8A7B6C5D4E3F2A1B... | Your_CUSTOM_PREFIX_9C8B7A6F5E4D3C2B... | Your_CUSTOM_PREFIX_7D6C5B4A3F2E1D0C... | Your_CUSTOM_PREFIX_6A5B4C3D2E1F0A9B... ``` **查询结果(解密后):** ```json { "id": 1, "name": "李四", "idCard": "110101199003073329", "phone": "13912345678", "email": "lisi@example.com" } ``` 2. MyBatis 自定义 SQL [#2-mybatis-自定义-sql] ```java @Data @TableName(value ="employee") public class EmployeeDO implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Long id; @TableField(value = "employee_id") @SecurityField private String employeeId; @TableField(value = "full_name") @SecurityField private String fullName; @TableField(value = "bank_account") @SecurityField private String bankAccount; } @Mapper public interface EmployeeMapper extends BaseMapper { List selectByDepartment(@Param("department") String department); int insertBatch(@Param("employees") List employees); } ``` ```xml INSERT INTO employee (employee_id, full_name, bank_account, department) VALUES (#{item.employeeId}, #{item.fullName}, #{item.bankAccount}, #{item.department}) ``` 🎭 综合场景示例 [#-综合场景示例] 1. 电商订单管理 [#1-电商订单管理] ```java @Data @TableName(value ="orders") public class OrderDO implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Long id; @TableField(value = "order_no") private String orderNo; @TableField(value = "customer_name") @SecurityField private String customerName; @TableField(value = "customer_phone") @SecurityField private String customerPhone; @TableField(value = "shipping_address") @SecurityField private String shippingAddress; @TableField(value = "total_amount") private BigDecimal totalAmount; @TableField(value = "status") private Integer status; } @Data public class OrderVO implements Serializable { private static final long serialVersionUID = 1L; private Long id; private String orderNo; @Masked(type = MaskType.NAME_MASK) private String customerName; @Masked(type = MaskType.PHONE_MASK) private String customerPhone; @Masked(type = MaskType.ADDRESS_MASK) private String shippingAddress; private BigDecimal totalAmount; private String status; } @RestController @RequestMapping("/api/orders") public class OrderController { @Autowired private OrderService orderService; @PostMapping("/save") public void saveOrder(@RequestBody OrderDO order) { orderService.saveOrder(order); } @GetMapping("/{id}") @Desensitizationed public OrderVO getOrder(@PathVariable Long id) { OrderDO orderDO = orderService.getOrder(id); return convertToVO(orderDO); } @GetMapping("/list") @Desensitizationed public List getOrders() { List orders = orderService.getOrders(); return orders.stream().map(this::convertToVO).collect(Collectors.toList()); } private OrderVO convertToVO(OrderDO orderDO) { OrderVO orderVO = new OrderVO(); orderVO.setId(orderDO.getId()); orderVO.setOrderNo(orderDO.getOrderNo()); orderVO.setCustomerName(orderDO.getCustomerName()); orderVO.setCustomerPhone(orderDO.getCustomerPhone()); orderVO.setShippingAddress(orderDO.getShippingAddress()); orderVO.setTotalAmount(orderDO.getTotalAmount()); // 状态转换 String statusText; switch (orderDO.getStatus()) { case 0: statusText = "待支付"; break; case 1: statusText = "已支付"; break; case 2: statusText = "已发货"; break; case 3: statusText = "已收货"; break; default: statusText = "未知"; } orderVO.setStatus(statusText); return orderVO; } } ``` **返回结果:** ```json { "id": 1, "orderNo": "ORD202401001", "customerName": "李*", "customerPhone": "139****5678", "shippingAddress": "上海市浦东新区****", "totalAmount": 299.00, "status": "已发货" } ``` ⚡ 高级用法示例 [#-高级用法示例] 1. 自定义切点表达式 [#1-自定义切点表达式] ```yaml rpamis: security: desensitization-enable: true # 自定义切点:所有标注了 @RestController 的类中的所有方法 custom-pointcut: '@within(org.springframework.web.bind.annotation.RestController)' ``` **说明:** 通过自定义切点表达式,可以实现更细粒度的脱敏控制。 2. 自定义加密密钥 [#2-自定义加密密钥] ```yaml rpamis: security: algorithm: active: sm4 sm4: # 自定义密钥(16位) key: YourCustomSecretKey1 # 自定义前缀 prefix: MY_SECURE_ ``` **说明:** 密钥是加密解密的核心,请确保密钥的安全性。 3. 自定义脱敏规则 [#3-自定义脱敏规则] ```java // 创建自定义脱敏函数 public class CustomMaskFunction implements MaskFunction { @Override public String mask(String value) { // 将敏感字段中的数字替换为星号 return value.replaceAll("[0-9]", "*"); } } // 使用自定义脱敏 @Data public class CardVO implements Serializable { @Masked(type = MaskType.CUSTOM_MASK, start = 0, end = value.length(), symbol = "#") private String cardNumber; } ``` # 介绍 import { Cards, Card } from '@/components/card'; import { Callout } from '@/components/callout'; import { Files, Folder, File } from '@/components/files'; 🎯 Rpamis-Security [#-rpamis-security] Rpamis-security 是基于 MyBatis 插件开发的企业级安全组件,提供注解式的数据脱敏、数据库自动加解密功能,旨在解决企业数据安全需求。 📋 项目概述 [#-项目概述] Rpamis-security 组件提供以下核心功能: 🏗️ 技术架构 [#️-技术架构] 📁 模块结构 [#-模块结构] 项目采用 Maven 多模块架构: * **rpamis-security-annotation**:📝 注解模块,定义所有安全相关的注解 * **rpamis-security-core**:⚙️ 核心功能模块,包含脱敏、加解密的具体实现 * **rpamis-security-spring-boot-starter**:🚀 Spring Boot 自动配置模块,简化集成 * **rpamis-security-test**:✅ 测试模块,包含完整的单元测试和示例代码 🔄 实现原理 [#-实现原理] 1. **数据脱敏**:🎯 基于 AOP 切面实现,拦截带有 `@Desensitizationed` 注解的方法 2. **数据库加解密**:🔌 基于 MyBatis 插件实现,在 SQL 执行过程中自动处理 3. **策略模式**:🏭 通过工厂类根据不同类型应用对应的脱敏或加密算法 Rpamis-Security 采用注解驱动的方式,最大程度减少侵入性,让开发者专注于业务逻辑开发。 🚀 快速上手 [#-快速上手] 📦 安装依赖 [#-安装依赖] 对于 **JDK 17 及以上** 使用版本 1.1.4: ```xml com.rpamis rpamis-security-spring-boot-starter 1.1.4 ``` 对于 **JDK 8-JDK 17** 使用版本 1.0.7: ```xml com.rpamis rpamis-security-spring-boot-starter 1.0.7 ``` ⚙️ 基础配置 [#️-基础配置] ```yaml rpamis: security: # 🚀 是否开启安全组件,落库加密,出库脱密 enable: true # 🛡️ 忽略解密失败,如果解密失败则返回原值 ignore-decrypt-failed: true # 👤 是否开启脱敏切面 desensitization-enable: true # 🎯 自定义切点,比如增加 RestController 切点 custom-pointcut: '@within(org.springframework.web.bind.annotation.RestController)' # 🔐 加解密算法配置 algorithm: active: sm4 sm4: key: 2U43wVWjLgToKBzG prefix: Your_CUSTOM_PREFIX_ ``` 💻 使用示例 [#-使用示例] 🔒 数据脱敏 [#-数据脱敏] ```java @Data public class UserVO implements Serializable { @Masked(type = MaskType.NAME_MASK) private String name; @Masked(type = MaskType.IDCARD_MASK) private String idCard; @Masked(type = MaskType.PHONE_MASK) private String phone; @Masked(type = MaskType.CUSTOM_MASK, start = 2, end = 5, symbol = "#") private String customFiled; } ``` 💾 数据库加解密 [#-数据库加解密] ```java @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 = "id_card") @SecurityField private String idCard; @TableField(value = "phone") @SecurityField private String phone; } ``` 🌟 项目特色 [#-项目特色] 📊 优势对比 [#-优势对比] | 特性 | Rpamis-Security | 同类项目 | | ----------------- | --------------- | --------- | | 任意实体类型脱敏 | ✅ 支持 | ❌ 仅支持单一实体 | | 任意实体类型嵌套脱敏 | ✅ 支持 | ❌ 不支持 | | 任意实体类型落库数据自动加解密 | ✅ 支持 | ❌ 仅支持单一实体 | | 动态 SQL 加解密 | ✅ 支持 | ❌ 不支持 | | 国家标准加密算法 SM4 | ✅ 支持 | ❌ 不支持 | | 脱敏、加解密多项选择可配置 | ✅ 支持 | ❌ 不支持 | | 新增入库后不改变源对象引用 | ✅ 支持 | ❌ 不支持 | | 可拓展式加解密算法、类型处理器 | ✅ 支持 | ❌ 不支持 | | 自定义脱敏标识、起始位置、结束位置 | ✅ 支持 | ❌ 不支持 | | 完整的单测用例 | ✅ 130+ 个 | ❌ 无 | Rpamis-Security 不仅功能全面,更有完整的 130+ 个单元测试用例保障代码质量,是企业级应用的可靠选择! 🤖 AI 辅助开发 [#-ai-辅助开发] Rpamis-Security 提供了 **Agent Skill** 支持,帮助开发者更高效地使用本组件。 如何使用? [#如何使用] 在支持 Agent Skill 的 AI 工具中,AI会自动识别并加载本项目的 Skill,为您提供: * 📖 **智能文档查询**:快速获取配置说明和使用示例 * 🔧 **代码生成建议**:根据您的需求生成合适的注解和配置 * 🐛 **问题诊断**:帮助排查加密、脱敏相关的问题 * 💡 **最佳实践推荐**:提供安全性和性能优化建议 获取 Skill [#获取-skill] Agent Skill 文件位于项目根目录的 `skill/rpamis-security/SKILL.md` Agent Skill 包含了从快速开始到高级特性的完整指南,是您使用 Rpamis-Security 的得力助手! 👥 社区支持 [#-社区支持] * **GitHub**:[https://github.com/rpamis/rpamis-security](https://github.com/rpamis/rpamis-security) * **Issues**:[https://github.com/rpamis/rpamis-security/issues](https://github.com/rpamis/rpamis-security/issues) * **Maven**:[https://central.sonatype.com/artifact/com.rpamis/rpamis-security-spring-boot-starter](https://central.sonatype.com/artifact/com.rpamis/rpamis-security-spring-boot-starter) 📄 许可证 [#-许可证] 该项目遵循 Apache 2.0 许可证。详细信息请查看 [LICENSE](/LICENSE) 文件。 # 快速开始 import { Steps, Step } from '@/components/steps'; import { Callout } from '@/components/callout'; import { Tabs, TabList, TabTrigger, TabContent } from '@/components/tabs'; 🚀 快速开始 [#-快速开始] 📋 环境要求 [#-环境要求] | 软件 | 版本要求 | | ----------- | ----------------------- | | Java | 8 或更高版本 | | Spring Boot | 2.x 或 3.x | | MyBatis | 3.x 或 MyBatis Plus 3.x+ | 对于 **JDK 17+** 使用版本 1.1.4,**JDK 8-JDK 17** 使用版本 1.0.7。 📦 依赖引入 [#-依赖引入] 根据您的项目构建工具选择相应的依赖配置。 在项目配置文件中添加 Rpamis-Security 依赖。 Maven [#maven] 对于 JDK 17 及以上使用版本 1.1.4: ```xml com.rpamis rpamis-security-spring-boot-starter 1.1.4 ``` 对于 JDK 8-JDK 17 使用版本 1.0.7: ```xml com.rpamis rpamis-security-spring-boot-starter 1.0.7 ``` Gradle [#gradle] ```groovy // JDK 17+ implementation 'com.rpamis:rpamis-security-spring-boot-starter:1.1.4' // JDK 8-JDK 17 implementation 'com.rpamis:rpamis-security-spring-boot-starter:1.0.7' ``` ⚙️ 配置文件 [#️-配置文件] 在 `application.yml` 或 `application.properties` 中添加以下配置: ```yaml rpamis: # 🛡️ rpamis-security 配置 security: # 是否开启安全组件,落库加密,出库脱密,如果不指定加密算法,则默认返回原值 # 当此开关为 false 时,无论脱敏切面是否开启,均不生效 enable: true # 忽略解密失败,如果解密失败则返回原值,否则抛出异常,如果不填写默认 true ignore-decrypt-failed: true # 是否开启脱敏切面 desensitization-enable: true # 自定义切点,比如增加 RestController 切点 custom-pointcut: '@within(org.springframework.web.bind.annotation.RestController)' # 🔐 加解密算法配置 algorithm: # 激活的加密算法 active: sm4 sm4: # 加密算法 key,需要自己生成,满足 16 位即可,下面只是样例 key: 1234567890123456 # 加解密唯一识别前缀,默认值为 ENC_SM4_ prefix: ENC_SM4_ ``` 📋 配置说明 [#-配置说明] 必需配置 [#必需配置] | 配置项 | 说明 | 类型 | 默认值 | | -------------------------------------- | ------------------ | ------- | ---------- | | `rpamis.security.enable` | 是否开启安全组件 | boolean | true | | `rpamis.security.algorithm.active` | 激活的加密算法(目前仅支持 sm4) | string | 无 | | `rpamis.security.algorithm.sm4.key` | SM4 算法密钥,必须满足 16 位 | string | 无 | | `rpamis.security.algorithm.sm4.prefix` | 加密字段前缀,用于标识已加密字段 | string | ENC\_SM4\_ | 可选配置 [#可选配置] | 配置项 | 说明 | 类型 | 默认值 | | ---------------------------------------- | ------------------ | ------- | ---- | | `rpamis.security.ignore-decrypt-failed` | 是否忽略解密失败,解密失败时返回原值 | boolean | true | | `rpamis.security.desensitization-enable` | 是否开启脱敏切面 | boolean | true | | `rpamis.security.custom-pointcut` | 自定义切点,用于 AOP 拦截 | string | "" | 💻 基础使用 [#-基础使用] 1. 数据脱敏 🎭 [#1-数据脱敏-] 在需要脱敏的字段上添加 @Masked 注解。 在需要脱敏的接口方法上添加 @Desensitizationed 注解。 调用接口验证脱敏效果。 在需要脱敏的字段上添加 `@Masked` 注解: ```java @Data public class UserVO implements Serializable { private static final long serialVersionUID = 1L; /** * 姓名 - 使用姓名脱敏规则 */ @Masked(type = MaskType.NAME_MASK) private String name; /** * 身份证号 - 使用身份证脱敏规则 */ @Masked(type = MaskType.IDCARD_MASK) private String idCard; /** * 手机号 - 使用手机号脱敏规则 */ @Masked(type = MaskType.PHONE_MASK) private String phone; /** * 银行卡号 - 使用银行卡脱敏规则 */ @Masked(type = MaskType.BANKCARD_MASK) private String bankCard; /** * 邮箱 - 使用邮箱脱敏规则 */ @Masked(type = MaskType.EMAIL_MASK) private String email; /** * 地址 - 使用地址脱敏规则 */ @Masked(type = MaskType.ADDRESS_MASK) private String address; /** * 自定义脱敏 - 从第2位开始到第5位,使用 # 作为标识符 */ @Masked(type = MaskType.CUSTOM_MASK, start = 2, end = 5, symbol = "#") private String customField; } ``` 在需要脱敏的接口方法上添加 `@Desensitizationed` 注解: ```java @RestController @RequestMapping("/api") public class UserController { @PostMapping("/user") @Desensitizationed public UserVO getUser() { UserVO user = new UserVO(); user.setName("张三"); user.setIdCard("110101199003073328"); user.setPhone("13812345678"); user.setBankCard("6222020200099998888"); user.setEmail("zhangsan@example.com"); user.setAddress("北京市朝阳区XX街道XX小区"); user.setCustomField("1234567890"); return user; } } ``` 2. 数据库加解密 🔒 [#2-数据库加解密-] 在需要加密的字段上添加 @SecurityField 注解。 使用 MyBatis 或 MyBatis Plus 进行数据库操作。 组件会自动处理加密入库和解密出库。 在需要加密的字段上添加 `@SecurityField` 注解: ```java @Data @TableName(value ="user") public class UserDO implements Serializable { private static final long serialVersionUID = 1L; @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; @TableField(value = "email") @SecurityField private String email; } ``` 当使用 MyBatis 或 MyBatis Plus 进行数据库操作时,字段会自动加密入库,查询时自动解密: ```java @Mapper public interface UserMapper extends BaseMapper { // 所有操作都会自动加解密 } @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); // 自动解密 } } ``` 🎯 验证结果 [#-验证结果] 脱敏结果 [#脱敏结果] 请求 `/api/user` 接口,返回结果: ```json { "name": "张*", "idCard": "110***********3328", "phone": "138****5678", "bankCard": "6222 **** **** 8888", "email": "zhan****@example.com", "address": "北京市朝阳区**********", "customField": "1####67890" } ``` 加密结果 [#加密结果] 数据库中的数据会被加密: ```sql -- 加密后的效果(实际值会因密钥和前缀不同而变化) Your_CUSTOM_PREFIX_8A7B6C5D4E3F2A1B... ``` ❓ 常见问题 [#-常见问题] 1. 为什么注解不生效? [#1-为什么注解不生效] * ✅ 配置文件是否开启了相应功能 * ✅ 注解所在的类或方法是否被 Spring 容器管理 * ✅ 类路径是否正确 * ✅ 是否添加了 @Desensitizationed 注解 2. 为什么脱敏没有效果? [#2-为什么脱敏没有效果] * 确保在 Controller 方法上添加了 @Desensitizationed 注解 * 检查配置文件中 desensitization-enable 是否为 true * 确保返回值是经过 Spring 容器管理的对象 3. 加密字段查询后是乱码? [#3-加密字段查询后是乱码] * 检查是否配置了正确的前缀 * 确保使用了相同的密钥和算法 * 检查是否启用了加解密功能 4. 支持哪些类型的返回值? [#4-支持哪些类型的返回值] * 单一实体类型 * `List` * `Map` * 统一返回体(如 `R`) * 嵌套类型(支持多层嵌套) 您已成功集成 Rpamis-Security!现在可以开始使用数据脱敏和数据库加解密功能了。