Spring Boot 可扩展脱敏框架设计全解析 | 注解+策略模式+模板方法模式实战

一、需求场景:为什么需要脱敏框架?

在数据安全合规要求下,敏感信息处理成为系统必备能力。典型场景:

  • 用户隐私保护(手机号、身份证、邮箱等)
  • 日志敏感信息过滤
  • 接口返回数据自动脱敏

传统方案痛点:

  • 硬编码脱敏逻辑,维护成本高
  • 不同字段需重复编写相似代码
  • 无法动态调整脱敏规则

二、框架设计全景图

脱敏注解 Jackson序列化器 脱敏处理器 正则处理器 滑动窗口处理器 自定义处理器

三、核心实现三步走

1. 注解体系设计(声明式配置)

顶级脱敏注解

java 复制代码
@Documented
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside // 此注解是其他所有 jackson 注解的元注解,打上了此注解的注解表明是 jackson 注解的一部分
@JsonSerialize(using = StringDesensitizeSerializer.class) // 指定序列化器
public @interface DesensitizeBy {

    /**
     * 脱敏处理器
     */
    @SuppressWarnings("rawtypes")
    Class<? extends DesensitizationHandler> handler();

}

邮箱脱敏注解

java 复制代码
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@DesensitizeBy(handler = EmailDesensitizationHandler.class)
public @interface EmailDesensitize {

    /**
     * 匹配的正则表达式
     */
    String regex() default "(^.)[^@]*(@.*$)";

    /**
     * 替换规则,邮箱;
     *
     * 比如:example@gmail.com 脱敏之后为 e****@gmail.com
     */
    String replacer() default "$1****$2";

    /**
     * 是否禁用脱敏
     *
     * 支持 Spring EL 表达式,如果返回 true 则跳过脱敏
     */
    String disable() default "";

}

2. 处理机制实现(策略模式+模板方法模式)

脱敏处理器

java 复制代码
public interface DesensitizationHandler<T extends Annotation> {

    /**
     * 脱敏
     *
     * @param origin     原始字符串
     * @param annotation 注解信息
     * @return 脱敏后的字符串
     */
    String desensitize(String origin, T annotation);

    /**
     * 是否禁用脱敏的 Spring EL 表达式
     *
     * 如果返回 true 则跳过脱敏
     *
     * @param annotation 注解信息
     * @return 是否禁用脱敏的 Spring EL 表达式
     */
    default String getDisable(T annotation) {
        // 约定:默认就是 enable() 属性。如果不符合,子类重写
        try {
            return (String) ReflectUtil.invoke(annotation, "disable");
        } catch (Exception ex) {
            return "";
        }
    }

}

正则表达式脱敏处理器抽象类(模板方法模式+策略模式)

java 复制代码
public abstract class AbstractRegexDesensitizationHandler<T extends Annotation>
        implements DesensitizationHandler<T> {
//==========================================不可变部分即公共方法即模板方法模式的实现===========================


    @Override
    public String desensitize(String origin, T annotation) {
        // 1. 判断是否禁用脱敏
        Object disable = SpringExpressionUtils.parseExpression(getDisable(annotation));
        if (Boolean.TRUE.equals(disable)) {
            return origin;
        }

        // 2. 执行脱敏
        String regex = getRegex(annotation);
        String replacer = getReplacer(annotation);
        return origin.replaceAll(regex, replacer);
    }


	//==========================================可变部分抽象由具体子类实现===========================

    /**
     * 获取注解上的 regex 参数
     *
     * @param annotation 注解信息
     * @return 正则表达式
     */
    abstract String getRegex(T annotation);

    /**
     * 获取注解上的 replacer 参数
     *
     * @param annotation 注解信息
     * @return 待替换的字符串
     */
    abstract String getReplacer(T annotation);

}

EmailDesensitize的脱敏处理器

java 复制代码
//==========================================邮箱脱敏策略===========================

public class EmailDesensitizationHandler extends AbstractRegexDesensitizationHandler<EmailDesensitize> {

    @Override
    String getRegex(EmailDesensitize annotation) {
        return annotation.regex();
    }

    @Override
    String getReplacer(EmailDesensitize annotation) {
        return annotation.replacer();
    }

}

3. Jackson序列化集成

脱敏序列化器

java 复制代码
public class StringDesensitizeSerializer extends StdSerializer<String> implements ContextualSerializer {

    @Getter
    @Setter
    private DesensitizationHandler desensitizationHandler;

    protected StringDesensitizeSerializer() {
        super(String.class);
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) {
        DesensitizeBy annotation = beanProperty.getAnnotation(DesensitizeBy.class);
        if (annotation == null) {
            return this;
        }
        // 创建一个 StringDesensitizeSerializer 对象,使用 DesensitizeBy 对应的处理器
        StringDesensitizeSerializer serializer = new StringDesensitizeSerializer();
        serializer.setDesensitizationHandler(Singleton.get(annotation.handler()));
        return serializer;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
        if (StrUtil.isBlank(value)) {
            gen.writeNull();
            return;
        }
        // 获取序列化字段
        Field field = getField(gen);

        // 自定义处理器
        DesensitizeBy[] annotations = AnnotationUtil.getCombinationAnnotations(field, DesensitizeBy.class);
        if (ArrayUtil.isEmpty(annotations)) {
            gen.writeString(value);
            return;
        }
        for (Annotation annotation : field.getAnnotations()) {
            if (AnnotationUtil.hasAnnotation(annotation.annotationType(), DesensitizeBy.class)) {
                value = this.desensitizationHandler.desensitize(value, annotation);
                gen.writeString(value);
                return;
            }
        }
        gen.writeString(value);
    }

    /**
     * 获取字段
     *
     * @param generator JsonGenerator
     * @return 字段
     */
    private Field getField(JsonGenerator generator) {
        String currentName = generator.getOutputContext().getCurrentName();
        Object currentValue = generator.getCurrentValue();
        Class<?> currentValueClass = currentValue.getClass();
        return ReflectUtil.getField(currentValueClass, currentName);
    }

}

四、使用示例:三步完成脱敏

1. 添加注解

java 复制代码
public class UserVO {
    @EmailDesensitize(replacer = "$1***$2") // 自定义替换规则
    private String email;
    
    @PhoneDesensitize // 使用默认手机号脱敏规则
    private String phone;
}

2. 返回结果对比

java 复制代码
// 原始数据
{
  "email": "test@example.com",
  "phone": "13812345678"
}

// 脱敏后
{
  "email": "t***@example.com",
  "phone": "138****5678"
}

3. 动态关闭脱敏(SpEL支持)

java 复制代码
@EmailDesensitize(disable = "${app.desensitize.disable:false}")
private String email;

五、设计亮点分析

1. 开闭原则实践

100% 0% 扩展成本对比 新增脱敏类型 修改核心逻辑

  • 新增类型:只需添加注解+处理器
  • 修改规则:调整注解参数即可

2. 性能优化点

  • 单例模式:处理器全局单例复用
  • 缓存机制:注解解析结果缓存
  • 懒加载:按需初始化处理器

3. 灵活配置能力

java 复制代码
// 多规则叠加示例
@EmailDesensitize(regex = "(?<=.{2}).", replacer = "*")
@ConditionalOnProperty("security.enabled")
private String email;
相关推荐
javachen__几秒前
SpringBoot整合P6Spy实现全链路SQL监控
spring boot·后端·sql
IT毕设实战小研6 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
一只爱撸猫的程序猿7 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
甄超锋7 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
武昌库里写JAVA10 小时前
JAVA面试汇总(四)JVM(一)
java·vue.js·spring boot·sql·学习
Pitayafruit11 小时前
Spring AI 进阶之路03:集成RAG构建高效知识库
spring boot·后端·llm
zru_960211 小时前
Spring Boot 单元测试:@SpyBean 使用教程
spring boot·单元测试·log4j
甄超锋12 小时前
Java Maven更换国内源
java·开发语言·spring boot·spring·spring cloud·tomcat·maven
还是鼠鼠12 小时前
tlias智能学习辅助系统--Maven 高级-私服介绍与资源上传下载
java·spring boot·后端·spring·maven
舒一笑17 小时前
Started TttttApplication in 0.257 seconds (没有 Web 依赖导致 JVM 正常退出)
jvm·spring boot·后端