业务数据有很多用户敏感数据,比如手机号、身份证号、姓名,报表系统对这些数据不加以处理,很容易被别人下载,导致用户信息泄露。作为开发人员,就需要对手机号、身份证号、姓名进行脱敏处理。
最简单的方法:
数据在返回给前端之前,遍历数据,对手机号、身份证号、姓名进行脱敏处理,如果有N张报表,我们就需要在N个controller层或者service层去处理,如果脱敏规则改了,就需要改N个controller层或者service层。
升级版:
将脱敏处理规则封装成一个方法,N个controller层或者service层,都使用这个方法,后续改动只改这一个方法,但是这样对controller层或者service层的侵入太强了,不符合高内聚低耦合的设计思想。
优雅版:
只在对应的对象上,指定对应的脱敏规则处理方法,在返回给前端的时候,使用com.fasterxml.jackson.databind.JsonSerializer进行序列化脱敏
具体代码:
1、引用jackson-databind,在springboot,这个包不需要单独引用
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.6.1</version>
</dependency>
2、定义指定脱敏规则的注解
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationJsonSerializer.class)
public @interface Desensitization {
Class<? extends BizDataDesensitization> value();
int userLevel() default 1;
}
3、重写脱敏序列化方法
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import java.io.IOException;
import java.util.Objects;
/**
* 重写脱敏序列化方法
*
* 主要是这两个方法
* com.cn.dl.springbootdemo.tuomin.DesensitizationJsonSerializer#serialize(java.lang.String, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
* com.cn.dl.springbootdemo.tuomin.DesensitizationJsonSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
*/
public class DesensitizationJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
private BizDataDesensitization desensitization;
//org.springframework.http.converter.HttpMessageNotWritableException:
//No converter found for return value of type: class com.cn.dl.springbootdemo.tuomin.User
/**
* 需要加上默认构造器,否则会抛出上面的异常
*/
public DesensitizationJsonSerializer() {
}
public DesensitizationJsonSerializer(BizDataDesensitization desensitization) {
this.desensitization = desensitization;
}
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(desensitization.serialize(s,jsonGenerator));
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
if(Objects.isNull(beanProperty)){
return serializerProvider.findNullValueSerializer(null);
}
if(! Objects.equals(beanProperty.getType().getRawClass(), String.class)){
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
if(Objects.equals(beanProperty.getType().getRawClass(), String.class)){
try {
//字段上有Desensitization注解的构造特定的脱敏函数
Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
if (Objects.nonNull(desensitization)) {
return new DesensitizationJsonSerializer(desensitization.value().newInstance());
}
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
return null;
}
}
4、处理脱敏字段接口
import com.fasterxml.jackson.core.JsonGenerator;
@FunctionalInterface
public interface BizDataDesensitization {
/**
* 处理脱敏字段接口
*
* @param value
* @param jsonGenerator
* @return
*/
String serialize(String value, JsonGenerator jsonGenerator);
}
5、身份证号脱敏规则
import com.fasterxml.jackson.core.JsonGenerator;
import java.util.Objects;
public class IdCardDesensitization implements BizDataDesensitization {
/**
* 身份证号脱敏
*
* 规则:只保留前三位、后四位
*
* @param idCardNo
* @param jsonGenerator
* @return
*/
@Override
public String serialize(String idCardNo, JsonGenerator jsonGenerator) {
if(Objects.isNull(idCardNo)){
return null;
}
return idCardNo.replaceAll("(?<=\\w{3})\\w(?=\\w{4})", "*");
}
}
6、手机号脱敏规则
import com.fasterxml.jackson.core.JsonGenerator;
import org.springframework.util.StringUtils;
public class MobileDesensitization implements BizDataDesensitization {
/**
* 手机号脱敏
*
* 规则:只保留前三位,后四位
*
* @param mobile
* @param jsonGenerator
* @return
*/
@Override
public String serialize(String mobile, JsonGenerator jsonGenerator) {
if (StringUtils.isEmpty(mobile) || (mobile.length() != 11)) {
return mobile;
}
return mobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
}
7、User 测试Bean
import java.io.Serializable;
/**
* User 测试Bean
*/
public class User implements Serializable {
private static final long serialVersionUID = -3541789110487459884L;
@Desensitization(IdCardDesensitization.class)
private String idCardNo;
@Desensitization(MobileDesensitization.class)
private String mobile;
private String address;
private Integer age;
public String getIdCardNo() {
return idCardNo;
}
public void setIdCardNo(String idCardNo) {
this.idCardNo = idCardNo;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
8、测试controller
@GetMapping(value = "/tuomin/demo")
public User studentInfoV1(){
try {
User user = new User();
user.setMobile("15799996666");
user.setIdCardNo("122833199808098989");
user.setAge(24);
user.setAddress("beijing");
return user;
} catch (Exception e){
e.printStackTrace();
}
return null;
}
9、测试结果