动态脱敏配置

241 阅读3分钟

1、前言

通过配置数据库需要脱敏的字段,实现对接平台字段的动态脱敏

2、前提

springboot返回浏览器之前默认通过jackson序列化为json返回前端,保证响应流是使用jackson工具将数据序列化,不可使用fastjson序列化,否则@JsonSerialize(using = SensitiveDataSerializer.class)配置不生效。

脱敏code:唯一,指向了平台的字段,code和字段为一一映射关系

规 则:【平台:模块:字段】 模块之前可以有父子级关系

3、编码实现

3.1自定义注解

@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@JacksonAnnotationsInside  
@Target(ElementType.FIELD)  
//jackson包的注解 指定自定义序列化类
@JsonSerialize(using = SensitiveDataSerializer.class)
public @interface SensitiveData {   
  
    /**  
    * 脱敏code  
    */  
    String sensitiveCode();  
}

3.2 自定义序列化器


@Slf4j  
@AllArgsConstructor  
@NoArgsConstructor  
public class SensitiveDataSerializer extends JsonSerializer<String> implements ContextualSerializer {  
  
    private SensitiveData sensitiveData;  
  
    //用来获取平台的脱敏配置
    private SensitiveTemplate sensitiveTemplate;  
  
 
    /**  
    * 添加@SensitiveData使用自定义的序列化规则  
    *  
    * @param prov prov  
    * @param property property  
    * @return JsonSerializer<?>  
    * @author xulongyu  
    * @date 2023/9/20 18:35  
    **/  
    @Override  
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {  
        SensitiveData annotation = property.getAnnotation(SensitiveData.class);  
        SensitiveTemplate bean = SpringUtil.getBean(SensitiveTemplate.class);  
        if (annotation != null) {  
            return new SensitiveDataSerializer(annotation, bean);  
        }  
        return this;  
        }  
  
    }
    
    /**  
    * 序列化规则  
    *  
    * @param value 原值  
    * @param gen gen  
    * @param serializers serializers  
    * @author xulongyu  
    * @date 2023/9/20 18:35  
    **/  
    @Override  
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) {  
        try {  
            if (StrUtil.isBlank(value) || null == sensitiveData) {  
                gen.writeString(value);  
                return;  
                }  
            String sensitiveCode = sensitiveData.sensitiveCode();  
            // 存放原始值  
            realValMap.put(sensitiveCode, value);  
            
            // code:Obj{字段|脱敏规则}
            Map<String, SensitiveConfigRes> config = sensitiveTemplate.getConfig();  
            SensitiveConfigRes sensitiveConfigRes = config.get(sensitiveCode);  
            if (null == sensitiveConfigRes) {  
                gen.writeString(value);  
                return;  
            }  
            
            //脱敏工具类
            String newValue = DesensitizeEnum.desensitizeByModel(sensitiveConfigRes.getDesensitizeMode(), value);  
            gen.writeString(newValue);  
           } catch (IOException e) {  
            log.info("序列化错误" + e.getMessage());  
            } finally {  
                try {  
                    gen.writeString(value);  
                } catch (IOException e) {  
                    log.info("序列化错误" + e.getMessage());  
                }  
            }  
}  

3.3 脱敏工具类

  
/**  
* 脱敏枚举工具类  
*  
* @author xulongyu  
* @date 2023/1/6 10:18  
**/  
public enum DesensitizeEnum {  
  
// 无  
NONE(){  
@Override  
public String desensitize(String content) {  
return content;  
}  
},  
//【手机号码】前三位,后四位,其他隐藏,比如135****6810  
PHONE() {  
@Override  
public String desensitize(String content) {  
return desValue(content, 3, 4, "*");  
}  
},  
  
//【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****  
ADDRESS() {  
@Override  
public String desensitize(String content) {  
return desValue(content, 6, 0, "*");  
}  
},  
  
//【身份证号】显示前六位, 四位,其他隐藏。共计18位或者15位,比如:340304*******1234  
ID_NUMBER() {  
@Override  
public String desensitize(String content) {  
return desValue(content, 3, 2, "*");  
}  
},  
  
//【全脱敏】密码的全部字符都用*代替,比如:******  
ALL() {  
/**  
* 根据内容脱敏  
*  
* @param content content  
* @return String  
* @author xulongyu  
* @date 2023/1/6 10:19  
**/  
@Override  
public String desensitize(String content) {  
if (content == null) {  
return null;  
}  
return "******";  
}  
};  
  
DesensitizeEnum() {  
}  
  
/**  
* 根据内容脱敏  
*  
* @param content content  
* @return String  
* @author xulongyu  
* @date 2023/1/6 10:19  
**/  
public String desensitize(String content) {  
return null;  
}  
  
  
/**  
* 根据脱敏模式进行脱敏  
*  
* @param desensitizeMode 脱敏模式  
* @param content 内容  
* @return String  
* @author xulongyu  
* @date 2023/1/6 10:22  
**/  
public static String desensitizeByModel(String desensitizeMode, String content) {  
if(StrUtil.isEmpty(content)){  
return StrPool.EMPTY;  
}  
for (DesensitizeEnum value : DesensitizeEnum.values()) {  
if (value.name().equals(desensitizeMode)) {  
return value.desensitize(content);  
}  
}  
return content;  
}  
  
/**  
* 对字符串进行脱敏操作  
*  
* @param origin 原始字符串  
* @param prefixNoMaskLen 左侧需要保留几位明文字段  
* @param suffixNoMaskLen 右侧需要保留几位明文字段  
* @param maskStr 用于遮罩的字符串, 如'*'  
* @return 脱敏后结果  
*/  
public static String desValue(String origin, int prefixNoMaskLen, int suffixNoMaskLen, String maskStr) {  
if (origin == null) {  
return null;  
}  
StringBuilder sb = new StringBuilder();  
for (int i = 0, n = origin.length(); i < n; i++) {  
if (i < prefixNoMaskLen) {  
sb.append(origin.charAt(i));  
continue;  
}  
if (i > (n - suffixNoMaskLen - 1)) {  
sb.append(origin.charAt(i));  
continue;  
}  
sb.append(maskStr);  
}  
return sb.toString();  
}  
}


4.jackson

jackson序列化为json字符串配置

ObjectMapper objectMapper = new ObjectMapper();  
// 保留null,空数组、空map 序列化
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);