脱敏原理
- 一个http 请求从发送到响应最少会有 2 次序列化操作,第一次是请求进来服务器时,会将请求数据序列化成 Java 对象,第二次是服务器响应时再次进行序列化讲 Java 对象转成json 对象
- 由此可以在数据响应前,重写序列化方法就可以对返回的Java 对象的字段进行脱敏
- 确定了脱敏时机,那就还需确认哪些字段需要脱敏,如果系统命名规范的话,那就可以直接统一通过反射获取字段名称进行匹配。不过一般不会有的,每个程序员都有各自的代码风格,如 name、username、userName等。所以在这里通过注解的方式最为方便。
- 在使用注解时,就可以确定哪个字段使用哪个脱敏的策略,详细代码事例如下
多敏策略
public enum SecretStrategy {
NAME(str -> str.replaceAll(".(?=.)", "*")),
ID_CARD(str -> str.replaceAll(".(?=.{4})", "*")),
PHONE(str -> str.replaceAll("(\d{3})\d{4}(\d{4})", "$1****$2"));
private final Function<String, String> desensitize;
public Function<String, String> getDesensitize() {
return desensitize;
}
SecretStrategy(Function<String, String> desensitize) {
this.desensitize = desensitize;
}
}
字段注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SecretJsonSerializer.class)
public @interface ProtectColumn {
SecretStrategy strategy() default SecretStrategy.NAME;
}
序列化器的实现
public class SecretJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
private SecretStrategy secretStrategy;
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
ProtectColumn annotation = beanProperty.getAnnotation(ProtectColumn.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, beanProperty.getType().getRawClass())) {
this.secretStrategy = annotation.strategy();
return this;
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (!Objects.isNull(secretStrategy) && !isPermitted()) {
s = secretStrategy.getDesensitize().apply(s);
}
jsonGenerator.writeString(s);
}
public boolean isPermitted() {
return false;
}
}
实体类使用
@Data
public class TestSensitive {
@ProtectColumn(strategy = SecretStrategy.NAME)
private String name;
@ProtectColumn(strategy = SecretStrategy.PHONE)
private String phone;
private int age;
}