com.lemon.mask.annotation;
/**
* @author lemon
* @description 数据脱敏注解
* @date 2020-07-01 19:00
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@JacksonAnnotationsInside
@JsonSerialize(using = JacksonDataMaskSerializer.class)
public @interface DataMask {
/**
* 脱敏数据类型
*/
SensitiveType sensitiveType() default SensitiveType.DEFAULT;
/**
* 开始N个字符不脱敏
*/
int prefixNoMaskSize() default Integer.MIN_VALUE;
/**
* 结尾N个字符不脱敏
*/
int suffixNoMaskSize() default Integer.MIN_VALUE;
/**
* 脱敏字符串
*/
String maskFlag() default "*";
}
com.lemon.mask.annotation;
/**
* @author lemon
* @description 数据脱敏类型注解
* @date 2020-07-01 19:00
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataMaskType {
/**
* 脱敏类型
*/
SensitiveType sensitiveType();
}
com.lemon.mask.builder;
import com.lemon.mask.annotation.DataMask;
import com.lemon.mask.strategy.DesensitizedConfig;
import com.lemon.mask.util.DesensitizedUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.Arrays;
/**
* @author lemon
* @description 数据脱敏:重写toString方法实现数据脱敏
* @date 2020-07-03 16:06
*/
public class DataMaskToStringBuilder extends ReflectionToStringBuilder {
private boolean excludeNullValues = true;
/**
* @param object
* @return
* @description
* @author lemon
* @date 2020-07-03 17:29
*/
public DataMaskToStringBuilder(Object object) {
super(object);
}
/**
* @param object
* @param style
* @return
* @description
* @author lemon
* @date 2020-07-03 17:29
*/
public DataMaskToStringBuilder(Object object, ToStringStyle style) {
super(object, style);
}
/**
* @param object
* @param style
* @return
* @description
* @author lemon
* @date 2020-07-03 17:29
*/
public DataMaskToStringBuilder(Object object, boolean excludeNullValues, ToStringStyle style) {
super(object, style);
this.excludeNullValues = excludeNullValues;
}
/**
* @param clazz
* @return void
* @description
* @author lemon
* @date 2020-07-03 17:29
*/
@Override
protected void appendFieldsIn(Class clazz) {
if (clazz.isArray()) {
this.reflectionAppendArray(this.getObject());
return;
}
Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
Arrays.stream(fields).forEach(this::appendFieldIn);
}
/**
* @param field
* @return void
* @description
* @author lemon
* @date 2020-07-03 17:32
*/
protected void appendFieldIn(Field field) {
String fieldName = field.getName();
if (!this.accept(field)) {
return;
}
try {
Object fieldValue = this.getValue(field);
if (this.excludeNullValues && fieldValue == null) {
return;
}
// 如果需要脱敏
DataMask dataMask = field.getAnnotation(DataMask.class);
if (dataMask == null || field.getType() != String.class) {
this.append(fieldName, fieldValue);
} else {
this.appendMaskFieldIn(fieldName, (String) fieldValue, dataMask);
}
} catch (IllegalAccessException ex) {
throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
}
}
/**
* @param fieldName
* @param fieldValue
* @param dataMask
* @return void
* @description
* @author lemon
* @date 2020-07-03 17:32
*/
protected void appendMaskFieldIn(String fieldName, String fieldValue, DataMask dataMask) {
DesensitizedConfig desensitizedConfig = DesensitizedUtils.getDesensitizedConfig(dataMask);
this.append(fieldName, DesensitizedUtils.maskValue(fieldValue, desensitizedConfig));
}
}
com.lemon.mask.enumeration;
/**
* @author lemon
* @description 数据脱敏类型
* @date 2020-07-03 14:15
*/
public enum SensitiveType {
/**
* 默认脱敏策略
*/
DEFAULT,
/**
* 中文名
*/
CHINESE_NAME,
/**
* 身份证号
*/
ID_CARD,
/**
* 座机号
*/
FIXED_PHONE,
/**
* 手机号
*/
MOBILE_PHONE,
/**
* 地址
*/
ADDRESS,
/**
* 电子邮件
*/
EMAIL,
/**
* 银行卡
*/
BANK_CARD,
/**
* 公司开户银行联号
*/
CNAPS_CODE,
/**
* 密码
*/
PASSWORD,
/**
* 车牌号
*/
CARNUMBER;
}
com.lemon.mask.serializer;
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 com.lemon.mask.annotation.DataMask;
import com.lemon.mask.strategy.DataMaskStrategy;
import com.lemon.mask.strategy.DataMaskStrategyContext;
import com.lemon.mask.strategy.DesensitizedConfig;
import com.lemon.mask.util.DesensitizedUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.Objects;
/**
* @author lemon
* @version 1.0
* @description: 服务返回数据脱敏
* @date Create by lemon on 2020-07-03 21:08
*/
@Slf4j
public class JacksonDataMaskSerializer extends JsonSerializer<String> implements ContextualSerializer {
private DesensitizedConfig desensitizedConfig;
public JacksonDataMaskSerializer() {
}
public JacksonDataMaskSerializer(DesensitizedConfig desensitizedConfig) {
this.desensitizedConfig = desensitizedConfig;
}
/**
* Method that can be called to ask implementation to serialize
* values of type this serializer handles.
*
* @param value Value to serialize; can <b>not</b> be null.
* @param jsonGenerator Generator used to output resulting Json content
* @param serializers Provider that can be used to get serializers for
*/
@Override
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException {
if (StringUtils.isBlank(value)) {
jsonGenerator.writeObject(value);
return;
}
if (desensitizedConfig == null) {
log.debug("Not mask");
jsonGenerator.writeObject(value);
return;
}
DataMaskStrategy maskStrategy = DataMaskStrategyContext.getDataMaskStrategy(desensitizedConfig.getSensitiveType());
if (maskStrategy == null) {
log.warn("No matching Mask Strategy found for type [{}]", desensitizedConfig.getSensitiveType());
jsonGenerator.writeObject(value);
return;
}
String newValue = maskStrategy.mask(desensitizedConfig, value);
log.debug("Find MaskStrategy [{}]", maskStrategy.getClass());
jsonGenerator.writeString(newValue);
}
/**
* Method called to see if a different (or differently configured) serializer
* is needed to serialize values of specified property.
* Note that instance that this method is called on is typically shared one and
* as a result method should <b>NOT</b> modify this instance but rather construct
* and return a new instance. This instance should only be returned as-is, in case
* it is already suitable for use.
*
* @param prov Serializer provider to use for accessing config, other serializers
* @param property Method or field that represents the property
* (and is used to access value to serialize).
* Should be available; but there may be cases where caller cannot provide it and
* null is passed instead (in which case impls usually pass 'this' serializer as is)
* @return Serializer to use for serializing values of specified property;
* may be this instance or a new instance.
* @throws JsonMappingException
*/
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
if (property == null) {
return prov.findNullValueSerializer(null);
}
if (Objects.equals(property.getType().getRawClass(), String.class)) {
DataMask dataMask = property.getAnnotation(DataMask.class);
if (dataMask == null) {
dataMask = property.getContextAnnotation(DataMask.class);
}
if (dataMask != null) {
DesensitizedConfig desensitizedConfig = DesensitizedUtils.getDesensitizedConfig(dataMask);
return new JacksonDataMaskSerializer(desensitizedConfig);
}
}
return prov.findValueSerializer(property.getType(), property);
}
}
com.lemon.mask.strategy;
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.ADDRESS)
public class Address implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:6}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:0}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return mask(original, prefixNoMaskSize, suffixNoMaskSize, maskFlag);
}
}
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.BANK_CARD)
public class BankCard implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:6}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:4}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return mask(original, prefixNoMaskSize, suffixNoMaskSize, maskFlag);
}
}
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: 【车牌号】前两位后一位,比如:京A****5
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.CARNUMBER)
public class CarNumber implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:2}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:1}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description 【车牌号】前两位后一位,比如:京A****5
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return mask(original, prefixNoMaskSize, suffixNoMaskSize, maskFlag);
}
}
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.CHINESE_NAME)
public class ChineseName implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:1}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:1}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
if (original.length() > 2) {
return mask(original, prefixNoMaskSize, suffixNoMaskSize, maskFlag);
} else {
return mask(original, 1, 0);
}
}
}
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.CNAPS_CODE)
public class CnapsCode implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:2}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:0}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return mask(original, prefixNoMaskSize, suffixNoMaskSize, maskFlag);
}
}
/**
* @author lemon
* @version 1.0
* @description: 脱敏配置
* @date Create by lemon on 2020-07-04 07:11
*/
public interface DataMaskConfig {
/**
* @param
* @return java.lang.String
* @description 前置不需要脱敏的长度
* @author lemon
* @date 2020-07-04 07:13
*/
int getPrefixNoMaskSize();
/**
* @param
* @return java.lang.String
* @description 后置不需要脱敏的长度
* @author lemon
* @date 2020-07-04 07:13
*/
int getSuffixNoMaskSize();
/**
* @param
* @return java.lang.String
* @description 脱敏字符串
* @author lemon
* @date 2020-07-04 07:13
*/
String getMaskFlag();
}
package com.lemon.mask.strategy;
import org.apache.commons.lang3.StringUtils;
/**
* @author lemon
* @version 1.0
* @description: 数据脱敏策略
* @date Create by lemon on 2020-07-03 14:26
*/
public interface DataMaskStrategy extends DataMaskConfig {
/**
* @param original
* @return java.lang.String
* @description 原始数据脱敏处理
* @author lemon
* @date 2020-07-03 14:51
*/
String mask(String original);
/**
* @param desensitizedConfig
* @param original
* @return java.lang.String
* @description 原始数据脱敏处理
* @author lemon
* @date 2020-07-04 07:08
*/
default String mask(DesensitizedConfig desensitizedConfig, String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return this.mask(original, desensitizedConfig.getPrefixNoMaskSize(), desensitizedConfig.getSuffixNoMaskSize(),
desensitizedConfig.getMaskFlag());
}
/**
* @param original 原始数据
* @param prefixNoMaskSize 开始N个字符不脱敏
* @param suffixNoMaskSize 结尾N个字符不脱敏
* @return java.lang.String
* @description
* @author lemon
* @date 2020-07-03 15:25
*/
default String mask(String original, int prefixNoMaskSize, int suffixNoMaskSize) {
if (StringUtils.isBlank(original)) {
return original;
}
return this.mask(original, prefixNoMaskSize, suffixNoMaskSize, "*");
}
/**
* @param original 原始数据
* @param prefixNoMaskSize 开始N个字符不脱敏
* @param suffixNoMaskSize 结尾N个字符不脱敏
* @param maskFlag 脱敏字符串
* @return java.lang.String
* @description
* @author lemon
* @date 2020-07-03 14:54
*/
default String mask(String original, int prefixNoMaskSize, int suffixNoMaskSize, String maskFlag) {
// 默认脱敏
if (prefixNoMaskSize == Integer.MIN_VALUE) {
prefixNoMaskSize = this.getPrefixNoMaskSize();
}
if (suffixNoMaskSize == Integer.MIN_VALUE) {
suffixNoMaskSize = this.getSuffixNoMaskSize();
}
// 空或者不需要脱敏
if (StringUtils.isBlank(original) || (prefixNoMaskSize < 1 && suffixNoMaskSize < 1)) {
return original;
}
if (StringUtils.isBlank(maskFlag)) {
maskFlag = this.getMaskFlag();
}
String left = "";
if (prefixNoMaskSize >= 1) {
left = StringUtils.left(original, prefixNoMaskSize);
}
String right = "";
if (suffixNoMaskSize >= 1) {
right = StringUtils.right(original, suffixNoMaskSize);
}
original = StringUtils.rightPad(left, (original.length() - suffixNoMaskSize), maskFlag) + right;
return original;
}
}
package com.lemon.mask.strategy;
import com.google.common.collect.Maps;
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* @author lemon
* @version 1.0
* @description: 数据脱敏策略
* @date Create by lemon on 2020-07-06 11:09
*/
@Slf4j
@Component
public class DataMaskStrategyContext implements InitializingBean {
@Autowired
private List<DataMaskStrategy> maskStrategys;
private static final Map<SensitiveType, DataMaskStrategy> MASK_STRATEGY_MAP = Maps.newHashMap();
@Override
public void afterPropertiesSet() throws Exception {
maskStrategys.forEach(maskStrategy -> {
DataMaskType maskType = maskStrategy.getClass().getAnnotation(DataMaskType.class);
if (maskType == null) {
log.error("Class[{}] must specify annotation [{}]", maskStrategy.getClass(), DataMaskType.class);
return;
}
MASK_STRATEGY_MAP.put(maskType.sensitiveType(), maskStrategy);
});
}
/**
* @param sensitiveType
* @return com.lemon.mask.strategy.DataMaskStrategy
* @description 获取数据脱敏策略
* @author lemon
* @date 2020-07-06 11:15
*/
public static DataMaskStrategy getDataMaskStrategy(SensitiveType sensitiveType) {
return MASK_STRATEGY_MAP.get(sensitiveType);
}
}
package com.lemon.mask.strategy;
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: 默认按照指定前后缀保留不脱敏数据
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.DEFAULT)
public class DefaultDataMask implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:0}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:0}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return mask(original, prefixNoMaskSize, suffixNoMaskSize, maskFlag);
}
}
package com.lemon.mask.strategy;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Data;
/**
* @author lemon
* @version 1.0
* @description: 脱敏配置
* @date Create by lemon on 2020-07-03 16:38
*/
@Data
public class DesensitizedConfig {
SensitiveType sensitiveType;
/**
* 前置不需要脱敏的长度
*/
int prefixNoMaskSize;
/**
* 后置不需要脱敏的长度
*/
int suffixNoMaskSize;
/**
* 脱敏字符串
*/
String maskFlag;
}
package com.lemon.mask.strategy;
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.EMAIL)
public class Email implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:1}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:0}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
int index = StringUtils.indexOf(original, "@");
if (index <= 1) {
return original;
} else {
String temp = mask(original.substring(0, index), prefixNoMaskSize, suffixNoMaskSize, maskFlag);
if (StringUtils.isBlank(temp)) {
return original;
}
return temp.concat(original.substring(index));
}
}
}
package com.lemon.mask.strategy;
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: [固定电话] 后四位,其他隐藏<例子:****1234>
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.FIXED_PHONE)
public class FixedPhone implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:0}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:4}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description [固定电话] 后四位,其他隐藏<例子:****1234>
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return mask(original, prefixNoMaskSize, suffixNoMaskSize, maskFlag);
}
}
package com.lemon.mask.strategy;
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.ID_CARD)
public class IdCard implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:0}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:4}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return mask(original, prefixNoMaskSize, suffixNoMaskSize, maskFlag);
}
}
package com.lemon.mask.strategy;
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: [手机号码] 前三位,后四位,其他隐藏<例子:123******6789>
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.MOBILE_PHONE)
public class MobilePhone implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:3}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:4}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:*}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description [手机号码] 前三位,后四位,其他隐藏<例子:123******6789>
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return mask(original, prefixNoMaskSize, suffixNoMaskSize, maskFlag);
}
}
package com.lemon.mask.strategy;
import com.lemon.mask.annotation.DataMaskType;
import com.lemon.mask.enumeration.SensitiveType;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author lemon
* @version 1.0
* @description: 中文名
* @date Create by lemon on 2020-07-03 14:53
*/
@Getter
@Component
@DataMaskType(sensitiveType = SensitiveType.PASSWORD)
public class Password implements DataMaskStrategy {
@Value("${mask.prefixNoMaskSize:0}")
private int prefixNoMaskSize;
@Value("${mask.suffixNoMaskSize:0}")
private int suffixNoMaskSize;
@Value("${mask.maskFlag:******}")
private String maskFlag;
/**
* @param original
* @return java.lang.String
* @description 原始数据脱敏处理
* @author lemon
* @date 2020-07-03 14:51
*/
@Override
public String mask(String original) {
if (StringUtils.isBlank(original)) {
return original;
}
return maskFlag;
}
/**
* @param original
* @param prefixNoMaskSize
* @param suffixNoMaskSize
* @param maskFlag
* @return java.lang.String
* @description
* @author lemon
* @date 2020-07-06 12:27
*/
@Override
public String mask(String original, int prefixNoMaskSize, int suffixNoMaskSize, String maskFlag) {
return this.mask(original);
}
}
com.lemon.mask.util;
package com.lemon.mask.util;
import com.google.common.base.Preconditions;
import com.lemon.mask.annotation.DataMask;
import com.lemon.mask.strategy.DataMaskStrategy;
import com.lemon.mask.strategy.DataMaskStrategyContext;
import com.lemon.mask.strategy.DesensitizedConfig;
import lombok.extern.slf4j.Slf4j;
/**
* @author lemon
* @description 敏感字符串遮罩
* @date 2020-07-01 19:01
*/
@Slf4j
public class DesensitizedUtils {
private DesensitizedUtils() {
}
/**
* @param dataMask
* @return com.lemon.mask.strategy.DesensitizedConfig
* @description
* @author lemon
* @date 2020-07-06 11:21
*/
public static DesensitizedConfig getDesensitizedConfig(DataMask dataMask) {
DesensitizedConfig desensitizedConfig = new DesensitizedConfig();
desensitizedConfig.setSensitiveType(dataMask.sensitiveType());
desensitizedConfig.setPrefixNoMaskSize(dataMask.prefixNoMaskSize());
desensitizedConfig.setSuffixNoMaskSize(dataMask.suffixNoMaskSize());
desensitizedConfig.setMaskFlag(dataMask.maskFlag());
return desensitizedConfig;
}
/**
* @param original
* @param desensitizedConfig
* @return java.lang.String
* @description 对字符串按指定类型脱敏
* @author lemon
* @date 2020-07-03 17:52
*/
public static String maskValue(String original, DesensitizedConfig desensitizedConfig) {
Preconditions.checkNotNull(desensitizedConfig.getSensitiveType(), "sensitive type should not be null.");
DataMaskStrategy maskStrategy = DataMaskStrategyContext.getDataMaskStrategy(desensitizedConfig.getSensitiveType());
if (maskStrategy == null) {
log.warn("No matching Mask Strategy found for type [{}]", desensitizedConfig.getSensitiveType());
return original;
}
log.debug("Find MaskStrategy [{}]", maskStrategy.getClass());
return maskStrategy.mask(desensitizedConfig, original);
}
}