Json脱敏
前言
博主github
博主个人博客http://blog.healerjean.com
这里有两种脱敏方式,一种是注解脱敏,另一种是字段名匹配脱敏
1、注解脱敏
1.1、注解
/**
* @author HealerJean
* @version 1.0v
* @ClassName SensitiveInfo
* @date 2019/6/13 20:01.
* @Description Json序列化脱敏注解
* 不建议使用注解,建议使用JsonUtils工具类进行脱敏,因为注解会让我们需要必要时输出到前台的信息变的脱敏
*/
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveInfoSerialize.class)
public @interface SensitiveInfo {
SensitiveTypeEnum value() ;
}
1.2、脱敏的类型枚举
package com.hlj.proj.utils.sensitivity;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SensitiveTypeEnum
* @date 2019/6/13 20:01.
* @Description 敏感信息枚举类型
*/
public enum SensitiveTypeEnum {
/**
* 身份证号
*/
ID_CARD,
/**
* 密码
*/
PASSWORD,
/**
* 手机号
*/
MOBILE_PHONE,
/**
* 电子邮件
*/
EMAIL,
/**
* 真实姓名
*/
NAME,
/**
* 账户信息
*/
ACCOUNT_NO;
}
1.3、Json脱敏序列化
package com.hlj.proj.utils.sensitivity;
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;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SensitiveInfoSerialize
* @date 2019/6/13 20:01.
* @Description Json脱敏序列化
*/
public class SensitiveInfoSerialize extends JsonSerializer<Object> implements ContextualSerializer {
private SensitiveTypeEnum type;
public SensitiveInfoSerialize() {
}
public SensitiveInfoSerialize(final SensitiveTypeEnum type) {
this.type = type;
}
@Override
public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException {
switch (this.type) {
case ID_CARD: {
jsonGenerator.writeString(SensitiveInfoUtils.idCard(String.valueOf(value)));
break;
}
case MOBILE_PHONE: {
jsonGenerator.writeString(SensitiveInfoUtils.mobilePhone(String.valueOf(value)));
break;
}
case EMAIL: {
jsonGenerator.writeString(SensitiveInfoUtils.email(String.valueOf(value)));
break;
}
case ACCOUNT_NO: {
jsonGenerator.writeString(SensitiveInfoUtils.acctNo(String.valueOf(value)));
break;
}
case PASSWORD: {
jsonGenerator.writeString(SensitiveInfoUtils.password(String.valueOf(value)));
break;
}
case NAME: {
jsonGenerator.writeString(SensitiveInfoUtils.realName(String.valueOf(value)));
break;
}
default:
jsonGenerator.writeString(String.valueOf(value));
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
if (beanProperty != null) {
// 非 String 类直接跳过
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
SensitiveInfo sensitiveInfo = beanProperty.getAnnotation(SensitiveInfo.class);
if (sensitiveInfo == null) {
sensitiveInfo = beanProperty.getContextAnnotation(SensitiveInfo.class);
}
// 如果能得到注解,就将注解的 value 传入 SensitiveInfoSerialize
if (sensitiveInfo != null) {
return new SensitiveInfoSerialize(sensitiveInfo.value());
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(beanProperty);
}
}
1.4、脱敏工具类
package com.hlj.proj.utils.sensitivity;
import org.apache.commons.lang3.StringUtils;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SensitiveInfoUtils
* @date 2019/6/13 20:01.
* @Description 脱敏工具类
*/
public class SensitiveInfoUtils {
/**
* [真实姓名] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
*/
public static String realName(final String realName) {
if (StringUtils.isBlank(realName)) {
return "";
}
return dealString(realName, 1, 0);
}
/**
* [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
*/
public static String idCard(final String idCard) {
if (StringUtils.isBlank(idCard)) {
return "";
}
return dealString(idCard, 3, 4);
}
/**
* [手机号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
*/
public static String mobilePhone(final String idCard) {
if (StringUtils.isBlank(idCard)) {
return "";
}
return dealString(idCard, 3, 4);
}
/**
* [邮箱] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
*/
public static String email(final String email) {
if (StringUtils.isBlank(email)) {
return "";
}
int index = email.indexOf("@");
return dealString(email, 3, email.length() - index);
}
/**
* [账号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
*/
public static String acctNo(final String idCard) {
if (StringUtils.isBlank(idCard)) {
return "";
}
final String name = StringUtils.left(idCard, 1);
return StringUtils.rightPad(name, StringUtils.length(idCard), "*");
}
/**
* [密码] 隐藏。<例子:*************>
*/
public static String password(final String password) {
if (StringUtils.isBlank(password)) {
return "";
}
return "*";
}
private static String dealString(String str, int head_off, int tail_off) {
int length = str.length();
StringBuffer sb = new StringBuffer();
final String head = StringUtils.left(str, head_off);
String tail = StringUtils.right(str, tail_off);
sb.append(head);
int size = length - (head_off + tail_off);
if (size > 0) {
while (size > 0) {
sb.append("*");
size--;
}
}
sb.append(tail);
return sb.toString();
}
/**
* 提供给外部进行直接脱敏处理
* @param type
* @param value
* @return
*/
public static String sensitveValue(SensitiveTypeEnum type, String value) {
switch (type) {
case NAME: {
return realName(String.valueOf(value));
}
case ID_CARD: {
return idCard(String.valueOf(value));
}
case MOBILE_PHONE: {
return mobilePhone(String.valueOf(value));
}
case EMAIL: {
return email(String.valueOf(value));
}
case ACCOUNT_NO: {
return acctNo(String.valueOf(value));
}
case PASSWORD: {
return password(String.valueOf(value));
}
default:
return String.valueOf(value);
}
}
}
1.5、Bean
package com.hlj.proj.dto.Demo;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.hlj.proj.utils.sensitivity.SensitiveInfo;
import com.hlj.proj.utils.sensitivity.SensitiveTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author HealerJean
* @version 1.0v
* @ClassName DemoDTO
* @date 2019/6/13 20:02.
* @Description
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "demo实体类")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DemoDTO {
@ApiModelProperty(value = "demo 主键", hidden = true)
private Long id;
@ApiModelProperty(value = "姓名")
@SensitiveInfo(SensitiveTypeEnum.REAL_NAME)
private String name;
@ApiModelProperty(value = "年龄")
private Integer age;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "是否删除,10可用,99删除 ", hidden = true)
private String delFlag;
@ApiModelProperty(value = "创建人", hidden = true)
private Long createUser;
@ApiModelProperty(value = "创建人名字", hidden = true)
private String createName;
@ApiModelProperty(value = "创建时间", hidden = true)
private java.util.Date createTime;
@ApiModelProperty(value = "更新人", hidden = true)
private Long updateUser;
@ApiModelProperty(value = "更新人名称", hidden = true)
private String updateName;
@ApiModelProperty(hidden = true)
private java.util.Date updateTime;
}
1.6、controller测试
/**
* 不建议使用注解
*
* @param demoDTO
* @return
*/
@ApiOperation(value = "注解脱敏:不建议使用",
notes = "注解脱敏:不建议使用",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE,
response = DemoDTO.class)
@GetMapping(value = "sensitivity/anno", produces = "application/json; charset=utf-8")
@ResponseBody
public ResponseBean anno(DemoDTO demoDTO) {
log.info("脱敏--------注解脱敏------数据信息{}", demoDTO);
demoEntityService.getDemoDTO(demoDTO);
return ResponseBean.buildSuccess(demoDTO);
}
1.7、浏览器观察
{
"success": true,
"result": {
"name": "张**",
"age": 1,
"phone": "17610397651",
"email": "healerjean@gmail.com"
},
"msg": "",
"code": 200,
"date": "1564458518959"
}
2、Json脱敏
2.1、提供给JsonUtils工具类进行脱敏
package com.hlj.proj.utils.sensitivity;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.util.List;
import java.util.Map;
/**
* @author HealerJean
* @version 1.0v
* @ClassName MyBeanSerializerModifier
* @date 2019/6/13 20:01.
* @Description 提供给JsonUtils工具类进行脱敏
*/
public class SensitiveSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
// 循环所有的beanPropertyWriter
for (int i = 0; i < beanProperties.size(); i++) {
BeanPropertyWriter writer = beanProperties.get(i);
//判断是否有注解
SensitiveInfo sensitiveInfo = writer.getAnnotation(SensitiveInfo.class);
if (sensitiveInfo != null) {
continue;
}
final String propName = writer.getName();
//正则匹配
for (Map.Entry<String, SensitiveTypeEnum> entry : SensitivityConstants.sensitivityRules.entrySet()) {
String rule = entry.getKey();
int length = rule.length();
int propLen = propName.length();
if (propName.length() < length) {
continue;
}
int temp = rule.indexOf("*");
String key = null;
String substring = null;
if (temp >= 0) {
if (temp < (length >> 2)) {
key = rule.substring(temp + 1, length);
substring = propName.substring(propLen - key.length(), propLen);
} else {
key = rule.substring(0, temp);
substring = propName.substring(0, temp);
}
if (substring.equals(key)) {
writer.assignSerializer(new SensitiveInfoSerialize(entry.getValue()));
}
} else if (rule.equals(propName)) {
writer.assignSerializer(new SensitiveInfoSerialize(entry.getValue()));
}
}
}
return beanProperties;
}
}
2.2、脱敏规则
package com.hlj.proj.utils.sensitivity;
import java.util.HashMap;
import java.util.Map;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SensitivityConstants
* @date 2019/6/13 20:01.
* @Description 脱敏规则
*/
public class SensitivityConstants {
public static Map<String, SensitiveTypeEnum> sensitivityRules = new HashMap<>();
static {
/** 真实姓名 */
sensitivityRules.put("name", SensitiveTypeEnum.NAME);
/** 身份证 */
sensitivityRules.put("*IdCard", SensitiveTypeEnum.ID_CARD);
sensitivityRules.put("idCard", SensitiveTypeEnum.ID_CARD);
/** 手机号 */
sensitivityRules.put("*Phone", SensitiveTypeEnum.MOBILE_PHONE);
sensitivityRules.put("*phone", SensitiveTypeEnum.MOBILE_PHONE);
sensitivityRules.put("phone", SensitiveTypeEnum.MOBILE_PHONE);
sensitivityRules.put("*Mobile", SensitiveTypeEnum.MOBILE_PHONE);
sensitivityRules.put("mobile", SensitiveTypeEnum.MOBILE_PHONE);
/** 邮箱 */
sensitivityRules.put("*Email", SensitiveTypeEnum.EMAIL);
sensitivityRules.put("email", SensitiveTypeEnum.EMAIL);
/** 密码 */
sensitivityRules.put("passWord", SensitiveTypeEnum.PASSWORD);
sensitivityRules.put("password", SensitiveTypeEnum.PASSWORD);
sensitivityRules.put("*Password", SensitiveTypeEnum.PASSWORD);
sensitivityRules.put("*PassWord", SensitiveTypeEnum.PASSWORD);
}
}
2.3、Json脱敏
ObjectMapper objectMapperSensitivity = new ObjectMapper();
//脱敏日志创建
objectMapperSensitivity.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapperSensitivity.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapperSensitivity.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapperSensitivity.registerModule(javaTimeModule);
objectMapperSensitivity.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//脱敏
objectMapperSensitivity.setSerializerFactory(objectMapperSensitivity.getSerializerFactory().withSerializerModifier(new SensitiveSerializerModifier()));
/**
* 对象转Json格式字符串----脱敏处理(包含map)
*
* @return
*/
public static String toJsonStringWithSensitivity(Object propName) {
if (propName != null && propName instanceof Map) {
Map map = (Map) propName;
if (map != null && !map.isEmpty()) {
Set<Map.Entry> set = map.entrySet();
for (Map.Entry item : set) {
Object key = item.getKey();
Object value = item.getValue();
if (key instanceof String) {
String keyString = key.toString();
String s = dealSensitivity(keyString, value.toString());
map.put(keyString, s);
}
}
}
}
try {
return objectMapperSensitivity.writeValueAsString(propName);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
private static String dealSensitivity(String mapkey, String mapValue) {
//正则匹配
for (Map.Entry<String, SensitiveTypeEnum> entry : SensitivityConstants.sensitivityRules.entrySet()) {
String rule = entry.getKey();
int length = rule.length();
int propLen = mapkey.length();
if (mapkey.length() < length) {
continue;
}
int temp = rule.indexOf("*");
String key = null;
String substring = null;
if (temp >= 0) {
if (temp < (length >> 2)) {
key = rule.substring(temp + 1, length);
substring = mapkey.substring(propLen - key.length(), propLen);
} else {
key = rule.substring(0, temp);
substring = mapkey.substring(0, temp);
}
if (substring.equals(key)) {
return SensitiveInfoUtils.sensitveValue(entry.getValue(), mapValue);
}
} else if (rule.equals(mapkey)) {
return SensitiveInfoUtils.sensitveValue(entry.getValue(), mapValue);
}
}
return mapValue;
}
2.4、Bean
package com.hlj.proj.dto.Demo;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.hlj.proj.utils.sensitivity.SensitiveInfo;
import com.hlj.proj.utils.sensitivity.SensitiveTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author HealerJean
* @version 1.0v
* @ClassName DemoDTO
* @date 2019/6/13 20:02.
* @Description
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "demo实体类")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DemoDTO {
@ApiModelProperty(value = "demo 主键", hidden = true)
private Long id;
@ApiModelProperty(value = "姓名")
@SensitiveInfo(SensitiveTypeEnum.REAL_NAME)
private String name;
@ApiModelProperty(value = "年龄")
private Integer age;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "是否删除,10可用,99删除 ", hidden = true)
private String delFlag;
@ApiModelProperty(value = "创建人", hidden = true)
private Long createUser;
@ApiModelProperty(value = "创建人名字", hidden = true)
private String createName;
@ApiModelProperty(value = "创建时间", hidden = true)
private java.util.Date createTime;
@ApiModelProperty(value = "更新人", hidden = true)
private Long updateUser;
@ApiModelProperty(value = "更新人名称", hidden = true)
private String updateName;
@ApiModelProperty(hidden = true)
private java.util.Date updateTime;
}
2.5、controlelr
/**
* 建议使用工具类
*
* @param demoDTO
* @return
*/
@ApiOperation(value = "Json工具脱敏:建议使用",
notes = "Json工具脱敏::建议使用",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE,
response = DemoDTO.class)
@GetMapping(value = "sensitivity/jsonUtils", produces = "application/json; charset=utf-8")
@ResponseBody
public String sensitivity(DemoDTO demoDTO) {
log.info("脱敏--------Json工具脱敏------数据信息{}", demoDTO);
demoEntityService.getDemoDTO(demoDTO);
return ResponseBean.buildSensitivitySuccess(demoDTO);
}
/**
* 脱敏
* @param result
* @return
*/
public static String buildSensitivitySuccess(Object result){
return JsonUtils.toJsonStringWithSensitivity(buildSuccess(result)) ;
}
2.6、浏览器测试
{
"success": true,
"result": {
"name": "张**",
"age": 1,
"phone": "176****7651",
"email": "hea*******@gmail.com"
},
"msg": "",
"code": 200,
"date": "1564460586464"
}
感兴趣的,欢迎添加博主微信
哈,博主很乐意和各路好友交流,如果满意,请打赏博主任意金额,感兴趣的在微信转账的时候,备注您的微信或者其他联系方式。添加博主微信哦。
请下方留言吧。可与博主自由讨论哦
微信 | 微信公众号 | 支付宝 |
---|---|---|