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

发布于:2025-04-02 ⋅ 阅读:(17) ⋅ 点赞:(0)

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

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

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

传统方案痛点:

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

二、框架设计全景图

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

三、核心实现三步走

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

顶级脱敏注解

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

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

}

邮箱脱敏注解

@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. 处理机制实现(策略模式+模板方法模式)

脱敏处理器

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 "";
        }
    }

}

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

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的脱敏处理器

//==========================================邮箱脱敏策略===========================

public class EmailDesensitizationHandler extends AbstractRegexDesensitizationHandler<EmailDesensitize> {

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

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

}

3. Jackson序列化集成

脱敏序列化器

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. 添加注解

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

2. 返回结果对比

// 原始数据
{
  "email": "test@example.com",
  "phone": "13812345678"
}

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

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

@EmailDesensitize(disable = "${app.desensitize.disable:false}")
private String email;

五、设计亮点分析

1. 开闭原则实践

100% 0% 扩展成本对比 新增脱敏类型 修改核心逻辑
  • 新增类型:只需添加注解+处理器
  • 修改规则:调整注解参数即可

2. 性能优化点

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

3. 灵活配置能力

// 多规则叠加示例
@EmailDesensitize(regex = "(?<=.{2}).", replacer = "*")
@ConditionalOnProperty("security.enabled")
private String email;


网站公告

今日签到

点亮在社区的每一天
去签到