2024-05-08
Java
00

目录

第一部分 概述
第二部分 Java中使用Jackson实现数据脱敏
2.1 常用数据脱敏规则
2.2 Jackson脱敏实现

第一部分 概述

数据脱敏是一种重要的数据安全技术,它通过特定的算法对敏感数据进行变形或替换,以保护个人隐私和企业机密,同时允许数据在开发、测试、分析等环境中使用,而不会带来安全风险。数据脱敏,也称为数据去隐私化,是指在给定脱敏规则和策略的情况下,对敏感数据(如手机号、银行卡号等)进行转换或修改的技术手段,防止敏感数据直接在不可靠的环境下使用。

数据脱敏分为静态数据脱敏(SDM)和动态数据脱敏(DDM):

  • 静态数据脱敏:适用于将数据从生产环境抽取出来后,在非生产环境中使用,如测试、开发、培训、数据分析等场景。
  • 动态数据脱敏:适用于生产环境,能够在访问敏感数据时实时进行脱敏处理,根据不同角色和权限执行不同的脱敏方案。

数据脱敏的实现方式

  • 使用脚本进行脱敏:早期用户通过编写代码或脚本来实现数据的脱敏变形,例如将敏感信息替换为其他信息。
  • 使用专业数据脱敏产品:随着信息化管理的完善和数据使用场景的复杂化,专业的数据脱敏产品因其高效性和准确性而成为主流选择。
  • 基于规则的脱敏:数据脱敏可以根据业务需求自行定义和编写脱敏规则,支持对特定敏感字段进行不落地脱敏。
  • 数据脱敏算法:包括替换、无效化、随机值、数据替换、对称加密、平均值处理、偏移和取整等。

数据脱敏的价值

  • 防止组织内部对隐私数据的滥用。
  • 防止隐私数据在未经脱敏的情况下从组织流出。
  • 满足企业保护隐私数据和保持监管合规的需要。
  • 降低数据泄露风险,提高数据的安全性。
  • 允许在保护隐私的前提下,对数据进行分析和使用,支持企业业务的连续性和创新。
  • 数据脱敏是企业数据安全管理不可或缺的一部分,对于保护个人隐私和企业数据资产具有重要意义。

第二部分 Java中使用Jackson实现数据脱敏

2.1 常用数据脱敏规则

  • 姓名: 两个字名称保留姓,名部分使用*替换,三个字(含)保留首尾两个字,其余部分使用*替换
  • 身份证号: 保留前6位与后3位,中间部分使用*替换
  • 手机号: 保留前3位与后4位,中间部分使用*替换
  • 邮箱: 保留前2位与@(含)后内容,中间部分使用*替换
  • 银行卡: 保留前4位与后4位,中间部分使用*替换

基于上述规则,将脱敏规则定义为枚举,示例代码如下:

java
/** * 敏感信息策略 */ public enum SensitiveStrategy { /** * 姓名 */ CHINESE_NAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")), /** * 身份证号 */ ID_CARD(s -> s.replaceAll("(\\d{6})\\d{9}(\\w{3})", "$1*********$2")), /** * 手机号 */ PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")), /** * 邮箱 */ EMAIL(s -> s.replaceAll("(\\w{2})\\w*(@[\\w|\\.]*)", "$1****$2")), /** * 银行卡 */ BANK_CARD_NO(s -> s.replaceAll("(\\w{4})\\w*(\\w{4})", "$1********$2")), /** * 自定义 */ CUSTOM(s -> s); private final Function<String, String> desensitizer; private SensitiveStrategy(Function<String, String> desensitizer) { this.desensitizer = desensitizer; } public String desensitized(String value) { return this.desensitizer.apply(value); } }

在上述示例代码中,处理定义的几种脱敏规则,额外增加了CUSTOM用于标识当现有规则不满足时的自定义处理。

提示

由于不同系统对数据脱敏的要求不同,上述脱敏规则仅为参考,实际应用中应根据实际情况进行调整。

2.2 Jackson脱敏实现

Jackson中支持用户自定义序列化,在脱敏实现中,针对不同的规则编写不同的序列化类显然是比较繁琐的,可以借助自定义注解的形式动态使用上一节中的脱敏规则枚举,在需要进行脱敏的字段上只需要添加脱敏注解即可。

接下来编写用于脱敏所需的注解,示例代码如下:

java
@Retention(RUNTIME) @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE }) @JacksonAnnotationsInside @JsonSerialize(using = SensitiveSerializer.class) public @interface Sensitive { SensitiveStrategy value(); String regex() default ""; String replacement() default ""; }

在脱敏注解中包含三个字段:

  • value: 表示使用的脱敏规则
  • regex: 当脱敏规则为CUSTOM时必填,用于定义自定义脱敏规则的正则表达式
  • replacement: 当脱敏规则为CUSTOM时必填,用于定义自定义脱敏规则的替换表达式

最后需要实现脱敏注解中使用的JsonSerialize注解对应的SensitiveSerializer实现,具体的脱敏逻辑应该在此类中处理,示例代码如下:

java
public class SensitiveSerializer extends StdScalarSerializer<String> implements ContextualSerializer { private Sensitive sensitive; public SensitiveSerializer(Sensitive sensitive) { super(String.class); this.sensitive = sensitive; } public SensitiveSerializer() { super(String.class); } @Override public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException { if (sensitive == null) { gen.writeString(value); return; } SensitiveStrategy strategy = sensitive.value(); if (SensitiveStrategy.CUSTOM == strategy) { gen.writeString(value.replaceAll(sensitive.regex(), sensitive.replacement())); return; } gen.writeString(strategy.desensitized(value)); } @Override public JsonSerializer<?> createContextual(SerializerProvider serializers, BeanProperty property) throws JsonMappingException { if (property == null) { return serializers.findNullValueSerializer(property); } // 非 String 类直接跳过 if (Objects.equals(property.getType().getRawClass(), String.class)) { Sensitive sensitive = property.getAnnotation(Sensitive.class); if (sensitive == null) { sensitive = property.getContextAnnotation(Sensitive.class); } if (sensitive != null) { return new SensitiveSerializer(sensitive); } } return serializers.findValueSerializer(property.getType(), property); } }

观察SensitiveSerializer的实现,与传统的自定义序列化不同,SensitiveSerializer实现了ContextualSerializer接口,该接口中定义了createContextual方法用于在序列化的时候,根据属性的脱敏注解动态创建实际的脱敏序列化实现。

最后,编写测试实体类对上述逻辑进行测试验证:

java
@Data @Accessors(chain = true) public static class User { @Sensitive(SensitiveStrategy.CHINESE_NAME) private String name; @Sensitive(SensitiveStrategy.ID_CARD) private String idCardNo; @Sensitive(SensitiveStrategy.PHONE) private String phone; @Sensitive(SensitiveStrategy.EMAIL) private String email; @Sensitive(SensitiveStrategy.BANK_CARD_NO) private String bankCardNo; } @Test public void testSensitive() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); User user = new User() .setName("蒋固金") .setIdCardNo("320324123456781452") .setPhone("15111111111") .setEmail("1013195469@qq.com") .setBankCardNo("6225881304837593"); System.out.println(mapper.writeValueAsString(user)); }

运行示例代码观察控制台输出

{ "name" : "蒋*金", "idCardNo" : "320324*********452", "phone" : "151****1111", "email" : "10****@qq.com", "bankCardNo" : "6225********7593" }

相关信息

Jackson库中,ContextualSerializer接口是一个用于自定义序列化器以支持更细粒度控制的接口。当需要基于对象的具体类型或者属性值来选择不同的序列化器时,这个接口就非常有用。 ContextualSerializer接口继承自JsonSerializer<T>接口,它要求实现一个createContextual()方法,该方法返回一个JsonSerializer<?>类型的实例,这个实例将用于实际的序列化过程。

主要作用和用途

  • 上下文感知序列化:允许开发者根据对象的上下文信息(比如对象的属性值、注解、甚至是运行时的类型)来决定使用哪个序列化器。
  • 多态序列化:在处理多态类层次结构时,可以根据子类的特定类型来选择不同的序列化器,从而实现更精细的序列化控制。
  • 动态配置:可以在序列化过程中动态地配置序列化器的行为,例如,根据属性的值来决定是否序列化该属性,或者如何序列化。
  • 注解驱动的序列化:结合Jackson的注解,可以利用ContextualSerializer来实现基于注解的序列化逻辑。
  • 性能优化:在某些情况下,可以实现一个高效的序列化器,它仅在需要时才进行额外的类型检查或上下文评估。

实现ContextualSerializer接口的类通常会与Jackson的注解处理器结合使用,以提供更灵活的序列化策略。例如,会定义一个带有@JsonSerialize(using = MyContextualSerializer.class)注解的类,其中MyContextualSerializer是一个实现了ContextualSerializer接口的类。

使用ContextualSerializer时,通常会重载createContextual()方法,以便于在该方法中进行必要的检查和配置,然后返回适当的JsonSerializer实例。这样Jackson在序列化对象时会调用这个createContextual()方法来获取正确的序列化器实例。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:蒋固金

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!