自定义注解结合策略模式实现数据脱敏

自定义注解结合策略模式实现数据脱敏

数据脱敏

常见的脱敏方式

基于成本的衡量,目前数据库层脱敏的不多,更多是前端脱敏,所以下面演示一种前端脱敏方案

前端脱敏实现

实现:后端把原始数据entity拿到后,放入vo类里面,而在 vo 类里给JavaBean字段标注注解 @Desensitive,后端返回前端之前,自动把这部分数据进行脱敏计算

代码实现

项目就是Springboot3+Mybatis-plus

pom 文件

java 复制代码
<?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>

启动类

java 复制代码
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication{
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

配置文件

java 复制代码
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.Driver

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.mapper-locations=classpath*:mapper/*.xml

controller、service、dao

java 复制代码
@RestController
@RequestMapping("/api/user")
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private 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(), "处理失败");
        }
    }
}
java 复制代码
@Component
public class DesensitiveComponent {
    @Autowired
    private 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);
                //拿到注解的type
                Desensive 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);
                    }
                }
            }
        }
    }
}
java 复制代码
/**
 * 脱敏的字段类型
 */
public enum DesensiveType {
    DEFAULT,
    CARD,
    PHONE,
    EMAIL
}
java 复制代码
public interface UserService extends IService<User> {
}
java 复制代码
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}
java 复制代码
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Desensive {
    DesensiveType type() default DesensiveType.DEFAULT;
}
java 复制代码
@TableName("t_user")
@Data
public class User {
    @TableId
    private Integer id;
    private String name;
    private Integer age;
    private String phone;
    private String card;
    private Date createTime;
}
java 复制代码
@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;
}
java 复制代码
public interface DesensiveStrategy {
    boolean support(DesensiveType type);
    String desensiveStrategy(String target);
}
java 复制代码
@Component
public class PhoneDesensiveStrategy implements DesensiveStrategy{
    @Override
    public boolean support(DesensiveType type) {
        return type.equals(DesensiveType.PHONE);
    }
    @Override
    public String desensiveStrategy(String phone) {
        if (phone.length() == 11){
            return  phone.substring(0, 3) + "****" + phone.substring(7, 11);
        }
        return "***********";
    }
}
java 复制代码
@Component
public class CardDesensiveStrategy implements DesensiveStrategy{
    @Override
    public boolean support(DesensiveType type) {
        return type.equals(DesensiveType.CARD);
    }

    @Override
    public String desensiveStrategy(String card) {
        if (card.length() > 10) {
            return card.substring(0, 6).concat("******");
        }
        return "~~~~~~~~";
    }
}

最终效果

相关推荐
有你有我OK4 小时前
springboot Admin 服务端 客户端配置
spring boot·后端·elasticsearch
xiaoopin6 小时前
简单的分布式锁 SpringBoot Redisson‌
spring boot·分布式·后端
霸道流氓气质13 小时前
SpringBoot+MybatisPlus+自定义注解+切面实现水平数据隔离功能(附代码下载)
java·spring boot·后端
韩立学长14 小时前
【开题答辩实录分享】以《智慧校园勤工俭学信息管理系统的设计与实现》为例进行答辩实录分享
vue.js·spring boot·微信小程序
克莱恩~莫雷蒂14 小时前
Spring Boot 中 controller层注解
java·spring boot·后端
fouryears_2341718 小时前
Redis缓存更新策略
java·spring boot·redis·spring
ChildrenGreens18 小时前
开箱即用的 Web 层解决方案:web-spring-boot-starter 助你统一返回体、异常处理与跨域配置
spring boot
计算机学姐18 小时前
基于SpringBoo+Vue的医院预约挂号管理系统【个性化推荐算法+可视化统计】
java·vue.js·spring boot·mysql·intellij-idea·mybatis·推荐算法
计算机学姐18 小时前
基于微信小程序的奶茶店点餐平台【2026最新】
java·vue.js·spring boot·mysql·微信小程序·小程序·mybatis