Java 敏感数据脱敏:自定义注解与 AOP 实现

1. 核心概念
  • 敏感数据脱敏:将敏感信息(如手机号、身份证号)转换为部分隐藏的非敏感形式
  • 自定义注解:标记需要脱敏的字段或方法
  • AOP(面向切面编程):在方法执行前后插入脱敏逻辑
2. 实现步骤
2.1 定义脱敏策略枚举
public enum SensitiveStrategy {
    PHONE,       // 手机号脱敏:138****1234
    ID_CARD,     // 身份证脱敏:110***********123X
    NAME,        // 姓名脱敏:张*三
    DEFAULT      // 默认脱敏:保留首尾字符
}

2.2 创建自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
    SensitiveStrategy strategy();
}

2.3 实现脱敏工具类
public class SensitiveUtil {
    public static String desensitize(String value, SensitiveStrategy strategy) {
        if (value == null) return null;
        switch (strategy) {
            case PHONE:
                return value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
            case ID_CARD:
                return value.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1**********$2");
            case NAME:
                return value.length() > 1 ? value.charAt(0) + "*" + value.substring(2) : value;
            default:
                return value.length() > 2 ? 
                    value.charAt(0) + "*".repeat(value.length()-2) + value.charAt(value.length()-1) : value;
        }
    }
}

2.4 实现AOP切面
@Aspect
@Component
public class SensitiveAspect {

    // 拦截所有Controller方法
    @Pointcut("execution(* com.example.controller..*(..))")
    public void controllerPointcut() {}

    // 后置处理返回结果
    @AfterReturning(pointcut = "controllerPointcut()", returning = "result")
    public Object doAfterReturning(Object result) {
        if (result == null) return null;
        return processObject(result);
    }

    // 递归处理对象
    private Object processObject(Object obj) {
        if (obj instanceof Collection) {
            return processCollection((Collection<?>) obj);
        } else if (obj instanceof Map) {
            return processMap((Map<?, ?>) obj);
        } else if (obj.getClass().isArray()) {
            return processArray((Object[]) obj);
        } else {
            return processSingleObject(obj);
        }
    }

    // 处理单个对象
    private Object processSingleObject(Object obj) {
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Sensitive.class)) {
                field.setAccessible(true);
                try {
                    Object value = field.get(obj);
                    if (value instanceof String) {
                        SensitiveStrategy strategy = field.getAnnotation(Sensitive.class).strategy();
                        String desensitized = SensitiveUtil.desensitize((String) value, strategy);
                        field.set(obj, desensitized);
                    }
                } catch (IllegalAccessException e) {
                    // 异常处理
                }
            }
        }
        return obj;
    }

    // 处理集合(省略Map/Array处理类似逻辑)
    private Collection<?> processCollection(Collection<?> collection) {
        return collection.stream()
                .map(this::processObject)
                .collect(Collectors.toList());
    }
}

3. 实体类应用示例
public class User {
    @Sensitive(strategy = SensitiveStrategy.NAME)
    private String name;
    
    @Sensitive(strategy = SensitiveStrategy.PHONE)
    private String phone;
    
    @Sensitive(strategy = SensitiveStrategy.ID_CARD)
    private String idCard;
    
    // 其他字段及getter/setter
}

4. 效果验证
@RestController
public class UserController {
    
    @GetMapping("/user")
    public User getUser() {
        // 实际应从数据库获取
        User user = new User();
        user.setName("张三");
        user.setPhone("13800138000");
        user.setIdCard("110101199001011234");
        return user;  // 返回时自动脱敏
    }
}

5. 输出结果
{
  "name": "张*",
  "phone": "138****8000",
  "idCard": "1101**********1234"
}

6. 优化建议
  1. 性能优化:对高频调用接口添加缓存机制
  2. 嵌套对象:增强递归处理深度
  3. 自定义规则:支持通过注解参数自定义脱敏规则
  4. 线程安全:确保脱敏工具类的线程安全性
  5. 日志监控:添加脱敏操作日志记录

此方案通过注解+AOP实现无侵入式脱敏,无需修改业务逻辑代码,只需在字段添加注解即可自动生效,符合开闭原则。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐