SQL实现
首先可以采用sql语句的形式:
利用 CONCAT()、LEFT()和RIGHT() 字符串函数组合使用,请看下面具体实现
-- CONCAT(str1,str2,…):返回结果为连接参数产生的字符串
-- LEFT(str,len):返回从字符串str 开始的len 最左字符
-- RIGHT(str,len):从字符串str 开始,返回最右len 字符
举个栗子:
-- 电话号码脱敏sql:
SELECT mobilePhone AS 脱敏前电话号码,CONCAT(LEFT(mobilePhone,3), '********' ) AS 脱敏后电话号码 FROM t_s_user
-- 身份证号码脱敏sql:
SELECT idcard AS 未脱敏身份证, CONCAT(LEFT(idcard,3), '****' ,RIGHT(idcard,4)) AS 脱敏后身份证号 FROM t_s_user
java实现
通过自定义注解的形式实现
提供两种方式:正则表达式和长度判定形式
举个栗子:对用户姓名进行脱敏
1.定义脱敏名称实体类
/**
* 中文名称脱敏
**/
public class SensitiveChineseName implements IStrategy {
@Override
public String desensitization(String source,int begin,int end) {
if(begin != SensitiveDefaultLengthEnum.CHINESE_NAME.getBegin() && begin !=0){
return SensitiveInfoUtils.chineseName(source,begin);
}
return SensitiveInfoUtils.chineseName(source, SensitiveDefaultLengthEnum.CHINESE_NAME.getBegin());
}
}
2.自定义工具类 实现统一管理进行数据处理
/**
* 数据脱敏的工具类
**/
public class SensitiveInfoUtils {
/**
* [中文姓名] 只显示第一(showLength)个汉字,其他隐藏为2个星号<例子:李**>
*/
public static String chineseName(final String fullName, int showLength) {
if (StringUtils.isBlank(fullName)) {
return "";
}
if(StringUtils.length(fullName) <= showLength){
return fullName;
}
final String name = StringUtils.left(fullName, showLength);
return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
}
}
3.定义枚举类 获取脱敏信息的类型
import lombok.Getter;
/**
* 枚举默认显示的长度
**/
public enum SensitiveDefaultLengthEnum {
/**
* 默认长度
*/
DEFAULT(0,0),
/**
*中文姓名
*/
CHINESE_NAME(1,0),
/**
* 密码
*/
PASSWORD(6,0),
/**
* 身份证号
*/
ID_CARD_NUM(0,4),
/**
* 固定电话
*/
FIXED_PHONE(0,4),
/**
* 电话
*/
MOBILE(3,4),
/**
* 地址
*/
ADDRESS(6,0),
/**
* 邮箱
*/
EMAIL(1,0),
/**
* 银行卡号
*/
BANKCARD(6,4),
/**
* 默认策略
*/
DEFAULT_STRATEGY(6,0),
;
SensitiveDefaultLengthEnum(int begin, int end){
this.begin = begin;
this.end = end;
};
/**
* 开始长度
*/
@Getter
private int begin;
/**
* 结束长度
*/
@Getter
private int end;
}
4.自定义注解实现
/**
* 序列化实现类
**/
@NoArgsConstructor
public class SensitiveInfoSerialize extends JsonSerializer<String> implements
ContextualSerializer {
/**
* 脱敏策略
*/
private IStrategy strategy;
/**
* 开始显示的字符长度
*/
private int begin;
/**
* 结尾显示的字符长度
*/
private int end;
/**
* 脱敏的正则
*/
private String pattern;
/**
* 替换后的字符
*/
private String replaceChar;
public SensitiveInfoSerialize(IStrategy strategy, String pattern, String replaceChar,int begin,int end){
this.strategy = strategy;
this.pattern = pattern;
this.replaceChar = replaceChar;
this.begin = begin;
this.end = end;
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
/// 默认使用正则脱敏、 begin、end 不为空,则策略脱敏
if(begin == 0 && end == 0){
gen.writeString(strategy.desensitizationByPattern(value,pattern,replaceChar));
}else{
gen.writeString(strategy.desensitization(value,begin,end));
}
}
@Override
@SneakyThrows
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);
}
if (sensitiveInfo != null) {
Class<? extends IStrategy> clazz = sensitiveInfo.strategy();
// 如果能得到注解,就将注解的 value 传入 SensitiveInfoSerialize
return new SensitiveInfoSerialize(clazz.getDeclaredConstructor().newInstance(),sensitiveInfo.pattern(),
sensitiveInfo.replaceChar(),sensitiveInfo.begin(),sensitiveInfo.end());
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
}
return serializerProvider.findNullValueSerializer(null);
}
}
/**
* 脱敏信息
*/
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveInfoSerialize.class)
@Inherited
public @interface SensitiveInfo {
/**
* 脱敏策略
* @return
*/
Class<? extends IStrategy> strategy() default DefaultSensitiveStrategy.class;
/**
* 输入格式,使用正则表达式, 优先级大于value
*
* @return 格式
*/
String pattern() default "";
/**
* 替换目标字符, 优先级大于value
*
* @return 替换目标字符串
*/
String replaceChar() default "";
/**
* 开始显示几位
* @return
*/
int begin() default 0;
/**
* 结束显示几位
* @return
*/
int end() default 0;
}
/**
* 中文姓名脱敏
**/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@SensitiveInfo(strategy = com.adru.sensitive.strategy.SensitiveChineseName.class, pattern = "(?<=.{1}).",replaceChar = "*")
@JacksonAnnotationsInside
public @interface SensitiveChineseName {
}
5.测试
定义一个测试类
/**
* 用户实体
**/
@Data
@Builder
public class UserEntity implements Serializable {
/**
* 中文姓名--正则
*/
@SensitiveChineseName
private String userNamePattern;
/**
* 中文姓名--长度
*/
@SensitiveInfo(strategy = com.adru.sensitive.strategy.SensitiveChineseName.class, begin = 1)
private String userNameLength;
}
@Test
public void test() {
UserEntity userEntity = UserEntity.builder()
.userNamePattern("张三丰")
.userNameLength("张三丰")
.build();
log.info(JsonMapper.nonNullMapper().toJson(userEntity));
}