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 {@Overridepublic 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;};/*** 开始长度*/@Getterprivate int begin;/*** 结束长度*/@Getterprivate int end;}
4.自定义注解实现
/*** 序列化实现类**/
@NoArgsConstructor
public class SensitiveInfoSerialize extends JsonSerializer<String> implementsContextualSerializer {/*** 脱敏策略*/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;}@Overridepublic 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@SneakyThrowspublic 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 传入 SensitiveInfoSerializereturn 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 {/*** 中文姓名--正则*/@SensitiveChineseNameprivate String userNamePattern;/*** 中文姓名--长度*/@SensitiveInfo(strategy = com.adru.sensitive.strategy.SensitiveChineseName.class, begin = 1)private String userNameLength;
}
@Testpublic void test() {UserEntity userEntity = UserEntity.builder().userNamePattern("张三丰").userNameLength("张三丰").build();log.info(JsonMapper.nonNullMapper().toJson(userEntity));}