一个注解搞定接口数据脱敏,太强了!

简介: 一个注解搞定接口数据脱敏,太强了!

下午惬意时光,突然产品小姐姐走到我面前,打断我短暂的摸鱼time,企图与我进行深入交流,还好我早有防备没有闪,打开瑞star的点单页面,暗示没有一杯coffee解决不了的需求,需求是某些接口返回的信息,涉及到敏感数据的必须进行脱敏操作,我思考一反,表示某问题,马上安排。

image.png

思路

1.要做成可配置多策略的脱敏操作,要不然一个个接口进行脱敏操作,重复的工作量太多,很显然违背了“多写一行算我输”的程序员规范,思来想去,定义数据脱敏注解和数据脱敏逻辑的接口, 在返回类上,对需要进行脱敏的属性加上,并指定对应的脱敏策略操作。


2.接下来我只需要拦截控制器返回的数据,找到带有脱敏注解的属性操作即可,一开始打算用@ControllerAdvice去实现,但发现需要自己去反射类获取注解,当返回对象比较复杂,需要递归去反射,性能一下子就会降低,于是换种思路,我想到平时使用的@JsonFormat,跟我现在的场景很类似,通过自定义注解跟字段解析器,对字段进行自定义解析,tql


代码

Spring Boot 基础就不介绍了,推荐下这个实战教程:

https://github.com/javastacks/spring-boot-best-practice

1. 自定义数据注解,并可以配置数据脱敏策略

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMasking {
    DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;
}

2. 自定义Serializer,参考jackson的StringSerializer,下面的示例只针对String类型进行脱敏

public interface DataMaskingOperation {
    String MASK_CHAR = "*";
    String mask(String content, String maskChar);
}
public enum DataMaskingFunc {
     /**
     *  脱敏转换器
     */
     NO_MASK((str, maskChar) -> {
        return str;
     }),
     ALL_MASK((str, maskChar) -> {
        if (StringUtils.hasLength(str)) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < str.length(); i++) {
                sb.append(StringUtils.hasLength(maskChar) ? maskChar : DataMaskingOperation.MASK_CHAR);
            }
            return sb.toString();
        } else {
            return str;
        }
    });
    private final DataMaskingOperation operation;
    private DataMaskingFunc(DataMaskingOperation operation) {
        this.operation = operation;
    }
    public DataMaskingOperation operation() {
        return this.operation;
    }
}
public final class DataMaskingSerializer extends StdScalarSerializer<Object> {
    private final DataMaskingOperation operation;
    public DataMaskingSerializer() {
        super(String.class, false);
        this.operation = null;
    }
    public DataMaskingSerializer(DataMaskingOperation operation) {
        super(String.class, false);
        this.operation = operation;
    }
    public boolean isEmpty(SerializerProvider prov, Object value) {
        String str = (String)value;
        return str.isEmpty();
    }
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (Objects.isNull(operation)) {
            String content = DataMaskingFunc.ALL_MASK.operation().mask((String) value, null);
            gen.writeString(content);
        } else {
            String content = operation.mask((String) value, null);
            gen.writeString(content);
        }
    }
    public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException {
        this.serialize(value, gen, provider);
    }
    public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
        return this.createSchemaNode("string", true);
    }
    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
        this.visitStringFormat(visitor, typeHint);
    }
}

3. 自定义AnnotationIntrospector,适配我们自定义注解返回相应的Serializer

@Slf4j
public class DataMaskingAnnotationIntrospector extends NopAnnotationIntrospector {
    @Override
    public Object findSerializer(Annotated am) {
        DataMasking annotation = am.getAnnotation(DataMasking.class);
        if (annotation != null) {
            return new DataMaskingSerializer(annotation.maskFunc().operation());
        }
        return null;
    }
}

4. 覆盖ObjectMapper

@Configuration(
        proxyBeanMethods = false
)
public class DataMaskConfiguration {
    @Configuration(
            proxyBeanMethods = false
    )
    @ConditionalOnClass({Jackson2ObjectMapperBuilder.class})
    static class JacksonObjectMapperConfiguration {
        JacksonObjectMapperConfiguration() {
        }
        @Bean
        @Primary
        ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
            ObjectMapper objectMapper = builder.createXmlMapper(false).build();
            AnnotationIntrospector ai = objectMapper.getSerializationConfig().getAnnotationIntrospector();
            AnnotationIntrospector newAi = AnnotationIntrospectorPair.pair(ai, new DataMaskingAnnotationIntrospector());
            objectMapper.setAnnotationIntrospector(newAi);
            return objectMapper;
        }
    }
}

5. 返回对象加上注解

public class User implements Serializable {
    /**
     * 主键ID
     */
    private Long id;
    /**
     * 姓名
     */
    @DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)
    private String name;
    /**
     * 年龄
     */
    private Integer age;
    /**
     * 邮箱
     */
    @DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)
    private String email;
}



相关文章
|
监控 安全 测试技术
敏感接口防刷规范及定责
在工作中有时候会遇到敏感接口被刷的情况,主要是这些敏感接口没有做防刷处理
|
开发框架 前端开发 网络协议
使用 DataAnnotations(数据注解)实现模型的通用数据校验
在实际项目开发中,无论任何方式、任何规模的开发模式,项目中都离不开对接入数据模型参数的合法性校验,目前普片的开发模式基本是前后端分离,当用户在前端页面中输入一些表单数据时,点击提交按钮,触发请求目标服务器的一系列后续操作,在这中间的执行过程中(标准做法推荐)无论...
43432 1
使用 DataAnnotations(数据注解)实现模型的通用数据校验
|
18天前
|
Java API 数据安全/隐私保护
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
48 1
|
3月前
|
SQL 安全 关系型数据库
一切都是为了安全!数据脱敏的 几种方案
一切都是为了安全!数据脱敏的 几种方案
42 0
|
5月前
|
IDE Java 数据库连接
JAVA注解:元数据,代码的“身份证”?!
【6月更文挑战第29天】Java注解,作为代码的“身份证”,提供元数据,用于编译时检查、自动生成代码和框架集成。例如,@Override确保方法重写正确,@Deprecated标记过时,@Autowired在Spring中实现依赖注入。Lombok的@Getter/@Setter简化getter/setter。注解提升代码质量和效率,是现代Java开发的关键实践。
30 0
|
5月前
|
Java
敏感词过滤
布隆过滤器用来判断一个集合中的是否包含某一个元素,由于采用hash运算,有hash碰撞的原因,所以会存在误判。布隆过滤器判定一个元素存在的情况,这个元素可能不存在,但是判定一个元素不存在的时候,是一定不存在的。
193 0
|
算法 安全
数据脱敏?看我一行注解搞定!
本文主要分享什么是数据脱敏,如何优雅的在项目中运用一个注解实现数据脱敏,为项目进行赋能。希望能给你们带来帮助。
1098 3
|
6月前
|
存储 安全 算法
十种接口安全方案!!!
日常开发中,如何保证接口数据的安全性呢?接口数据安全的保证过程,主要体现在这几个方面:一个就是数据传输过程中的安全,还有就是数据到达服务端,如何识别数据,最后一点就是数据存储的安全性。介绍下保证接口数据安全的10个方案。数据加签:用Hash算法(如MD5,或者SHA-256)把原始请求参数生成报文摘要,然后用私钥对这个摘要进行加密,就得到这个报文对应的数字签名sign(这个过程就是加签通常来说呢,请求方会把数字签名和报文原文一并发送给接收方。验签:接收方拿到原始报文和数字签名(sign)后,用。
316 1
|
JSON Java 数据库连接
一个注解优雅的实现 接口数据脱敏
一个注解优雅的实现 接口数据脱敏
|
消息中间件 JavaScript 小程序
仅需一个注解,实现 SpringBoot 项目中的隐私数据脱敏!
仅需一个注解,实现 SpringBoot 项目中的隐私数据脱敏!