数据脱敏是一种重要的数据安全技术,它通过特定的算法对敏感数据进行变形或替换,以保护个人隐私和企业机密,同时允许数据在开发、测试、分析等环境中使用,而不会带来安全风险。数据脱敏,也称为数据去隐私化,是指在给定脱敏规则和策略的情况下,对敏感数据(如手机号、银行卡号等)进行转换或修改的技术手段,防止敏感数据直接在不可靠的环境下使用。
数据脱敏分为静态数据脱敏(SDM
)和动态数据脱敏(DDM
):
数据脱敏的实现方式
数据脱敏的价值
*
替换,三个字(含)保留首尾两个字,其余部分使用*
替换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
用于标识当现有规则不满足时的自定义处理。
提示
由于不同系统对数据脱敏的要求不同,上述脱敏规则仅为参考,实际应用中应根据实际情况进行调整。
在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 "";
}
在脱敏注解中包含三个字段:
CUSTOM
时必填,用于定义自定义脱敏规则的正则表达式CUSTOM
时必填,用于定义自定义脱敏规则的替换表达式最后需要实现脱敏注解中使用的JsonSerialize
注解对应的SensitiveSerializer
实现,具体的脱敏逻辑应该在此类中处理,示例代码如下:
javapublic 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<?>
类型的实例,这个实例将用于实际的序列化过程。
主要作用和用途
ContextualSerializer
来实现基于注解的序列化逻辑。实现ContextualSerializer
接口的类通常会与Jackson
的注解处理器结合使用,以提供更灵活的序列化策略。例如,会定义一个带有@JsonSerialize(using = MyContextualSerializer.class)
注解的类,其中MyContextualSerializer
是一个实现了ContextualSerializer
接口的类。
使用ContextualSerializer
时,通常会重载createContextual()
方法,以便于在该方法中进行必要的检查和配置,然后返回适当的JsonSerializer
实例。这样Jackson
在序列化对象时会调用这个createContextual()
方法来获取正确的序列化器实例。
本文作者:蒋固金
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!