自定义注解结合策略模式实现数据脱敏
自定义注解结合策略模式实现数据脱敏
- 数据脱敏
- 常见的脱敏方式
- 前端脱敏实现
- 代码实现
- pom 文件
- 启动类
- 配置文件
- controller、service、dao
- 最终效果
数据脱敏

常见的脱敏方式

基于成本的衡量,目前数据库层脱敏的不多,更多是前端脱敏,所以下面演示一种前端脱敏方案
前端脱敏实现
实现:后端把原始数据entity拿到后,放入vo类里面,而在 vo 类里给JavaBean字段标注注解 @Desensitive,后端返回前端之前,自动把这部分数据进行脱敏计算

代码实现
项目就是Springboot3+Mybatis-plus
pom 文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example.springbootV3</groupId><artifactId>springbootV3</artifactId><version>0.0.1-SNAPSHOT</version><name>springbootV3</name><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.4.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
启动类
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication{public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
配置文件
spring.datasource.url=jdbc:mysql://192.168.133.128:3306/wxpay?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Drivermybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.mapper-locations=classpath*:mapper/*.xml
controller、service、dao
@RestController
@RequestMapping("/api/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DesensitiveComponent desensitiveComponent;@GetMapping("selectById/{id}")public ResultData<UserVo> selectById(@PathVariable("id") Integer id){try {User user = userService.getById(id);UserVo userVo = new UserVo();desensitiveComponent.desensitive(user, userVo);return ResultData.success(userVo);} catch (IllegalAccessException e) {return ResultData.fail(ReturnCodeEnum.HANDLE_FAILED.getCode(), "处理失败");}}
}
@Component
public class DesensitiveComponent {@Autowiredprivate List<DesensiveStrategy> desensiveStrategies;public void desensitive(Object source, Object vo) throws IllegalAccessException {// 将eneity属性拷贝到 vo 中BeanUtils.copyProperties(source, vo);// 给vo进行脱敏Field[] fields = vo.getClass().getDeclaredFields();// 遍历所有字段,判断是否有 @Desesitive注解for (Field field : fields) {if (field.isAnnotationPresent(Desensive.class)) {field.setAccessible(true);Object fieldValue = field.get(vo);//拿到注解的typeDesensive annotation = field.getAnnotation(Desensive.class);DesensiveType type = annotation.type();// 根据不同的类型调用不同的脱敏策略for (DesensiveStrategy stratege : desensiveStrategies) {if (stratege.support(type)) {String result = stratege.desensiveStrategy((String) fieldValue);field.set(vo, result);}}}}}
}
/*** 脱敏的字段类型*/
public enum DesensiveType {DEFAULT,CARD,PHONE,EMAIL
}
public interface UserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Desensive {DesensiveType type() default DesensiveType.DEFAULT;
}
@TableName("t_user")
@Data
public class User {@TableIdprivate Integer id;private String name;private Integer age;private String phone;private String card;private Date createTime;
}
@Data
public class UserVo {private Integer id;private String name;private Integer age;@Desensive(type = DesensiveType.PHONE)private String phone;@Desensive(type = DesensiveType.CARD)private String card;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date createTime;
}
public interface DesensiveStrategy {boolean support(DesensiveType type);String desensiveStrategy(String target);
}
@Component
public class PhoneDesensiveStrategy implements DesensiveStrategy{@Overridepublic boolean support(DesensiveType type) {return type.equals(DesensiveType.PHONE);}@Overridepublic String desensiveStrategy(String phone) {if (phone.length() == 11){return phone.substring(0, 3) + "****" + phone.substring(7, 11);}return "***********";}
}
@Component
public class CardDesensiveStrategy implements DesensiveStrategy{@Overridepublic boolean support(DesensiveType type) {return type.equals(DesensiveType.CARD);}@Overridepublic String desensiveStrategy(String card) {if (card.length() > 10) {return card.substring(0, 6).concat("******");}return "~~~~~~~~";}
}
最终效果

