项目开发中用户数据脱敏如何实现?

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));
	}

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ADRU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值