Springboot使用自定义注解返回数据脱敏及整合FastJson

最初的想法是通过在实体类的getter方法操作,让它从数据库返回值过后运用自己的脱敏策略重新赋值
在这里插入图片描述

不过这种方法有弊端,如果在后端还要使用值的话,拿到的值不是数据库的真是的数据,所以只能在springmvc返回前端的时候操作(如果从数据库后获取值不操作的话,可以直接在getter方法上面写)

下面采用自定义注解和拦截器的方式

引入fastjson依赖

<!-- fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.56</version>
</dependency>

枚举管理需要脱敏字段类型

/**
 * @Description 敏感类型
 * @Date 2020-08-26 12:36
 * @Created by zfy
 */
public enum SensitiveTypeEnum {

    PLATE_NUM,//车牌号
    
    ID_CARD,//身份证号

    PHONE;//手机号
}

脱敏工具类,操作各返回的形式

public class DesensitizationUtils {

    public static String hiddenPhone(String phone) {//手机号 隐藏中间四位
        if (StringUtils.isBlank(phone)) {
            return "";
        }
        return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
    }

    public static String hiddenPlateNum(String plateNum) {//车牌号 隐藏中间位
        if (StringUtils.isBlank(plateNum)) {
            return "";
        }
        return plateNum.substring(0, 3) + "*" + plateNum.substring(4, plateNum.length());
    }

    public static String hiddenIdCard(String idCard) {//身份证展示 前6位和后6位
        if (StringUtils.isBlank(idCard)) {
            return "";
        }
        return StringUtils.left(idCard, 6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(idCard, 4), StringUtils.length(idCard), "*"), "***"));
    }

}

使用自定义注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface Desensitization {
    //脱敏类型
    SensitiveTypeEnum type();
}

创建格式化类实现Formatter接口

public class DesensitizationFormatter implements Formatter<String> {
    private SensitiveTypeEnum typeEnum;

    public SensitiveTypeEnum getTypeEnum() {
        return typeEnum;
    }

    public void setTypeEnum(SensitiveTypeEnum typeEnum) {
        this.typeEnum = typeEnum;
    }

    @Override
    public String parse(String valueStr, Locale locale) throws ParseException {
        if (StringUtils.isNotBlank(valueStr)) {
            switch (typeEnum) {
                case ID_CARD:
                    valueStr = DesensitizationUtils.hiddenIdCard(valueStr);
                    break;
                case PHONE:
                    valueStr = DesensitizationUtils.hiddenPhone(valueStr);
                    break;
                case PLATE_NUM:
                    valueStr = DesensitizationUtils.hiddenPlateNum(valueStr);
                    break;
                default:
            }
        }
        return valueStr;
    }

    @Override
    public String print(String s, Locale locale) {
        return s;
    }
}

实现AnnotationFormatterFactory接口

public class DesensitizationAnnotationFormatterFactory implements AnnotationFormatterFactory<Desensitization> {

    @Override
    public Set<Class<?>> getFieldTypes() {
        Set<Class<?>> hashSet = new HashSet<>();
        hashSet.add(String.class);
        return hashSet;
    }

    @Override
    public Printer<?> getPrinter(Desensitization annotation, Class<?> fieldType) {
        return getFormatter(annotation);
    }

    @Override
    public Parser<?> getParser(Desensitization annotation, Class<?> fieldType) {
        return getFormatter(annotation);
    }

    private DesensitizationFormatter getFormatter(Desensitization desensitization) {
        DesensitizationFormatter formatter = new DesensitizationFormatter();
        formatter.setTypeEnum(desensitization.type());
        return formatter;
    }

}

springmvc配置文件

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatterForFieldAnnotation(new DesensitizationAnnotationFormatterFactory());
    }
}

实体类

@Data
@TableName("t_user")
public class User {

    @TableId
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private Long id;

    @TableField(strategy = FieldStrategy.NOT_EMPTY)
    private String username;

    @TableField(strategy = FieldStrategy.NOT_EMPTY)
    @Desensitization(type= SensitiveTypeEnum.PLATE_NUM)
    private String plateNum;

    @TableField(strategy = FieldStrategy.NOT_EMPTY)
    @Desensitization(type= SensitiveTypeEnum.PHONE)
    private String phone;

    @TableField(strategy = FieldStrategy.NOT_EMPTY)
    @Desensitization(type= SensitiveTypeEnum.ID_CARD)
    private String idCard;

    @TableField(strategy = FieldStrategy.NOT_EMPTY)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
}

Controller

@RestController
@RequestMapping("/api/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping
    private List<User> list() {
        return userService.list();
    }

    @PostMapping
    public String add(@RequestBody User user) {
        user.setCreateTime(new Date());
        userService.save(user);
        return "ok";
    }
}

Postman调用

在这里插入图片描述好像没什么卵用。。

添加一个拦截器并配置到消息转换器中

public class ValueDesensitizeFilter implements ValueFilter {
    @Override
    public Object process(Object object, String name, Object value) {
        if (null == value || !(value instanceof String) || ((String) value).length() == 0) {
            return value;
        }
        try {
            Field field = object.getClass().getDeclaredField(name);
            Desensitization desensitization;
            if (String.class != field.getType() || (desensitization = field.getAnnotation(Desensitization.class)) == null) {
                return value;
            }
            String valueStr = (String) value;
            SensitiveTypeEnum type = desensitization.type();
            switch (type) {
                case ID_CARD:
                    return DesensitizationUtils.hiddenIdCard(valueStr);
                case PHONE:
                    return DesensitizationUtils.hiddenPhone(valueStr);
                case PLATE_NUM:
                    return DesensitizationUtils.hiddenPlateNum(valueStr);
                default:
            }
        } catch (NoSuchFieldException e) {
            return value;
        }
        return value;
    }
}

在mvc配置类中加一个消息转换器

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {


    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatterForFieldAnnotation(new DesensitizationAnnotationFormatterFactory());
    }

    @Bean
    public HttpMessageConverters fastJsonHttpMessageConverters() {
        // 1.定义一个converters转换消息的对象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        // 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json数据
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);
        fastJsonConfig.setSerializeFilters(new ValueDesensitizeFilter());//添加自己写的拦截器
        // 3.在converter中添加配置信息
        fastConverter.setFastJsonConfig(fastJsonConfig);
        // 4.将converter赋值给HttpMessageConverter
        HttpMessageConverter<?> converter = fastConverter;
        // 5.返回HttpMessageConverters对象
        return new HttpMessageConverters(converter);
    }

}

在这里插入图片描述
这样子脱敏成功了,不过时间问题用的JsonFormat为jackson里面的注解,消息转换器用的是alibaba的Fastjson,所以原来的不起作用,处理方式为在实体类字段上的JsonFormat改成JsonField如图
在这里插入图片描述
还有id为Long类型,因为之前JsonFormat转字符串的注解失效所以在项目中取到的值有精度丢失情况(这儿postman调用没有精度丢失,项目测试时候看到浏览器preview中取到的id精度丢失),处理方式有两种:

1.在消息转换器中加上Long类型返回为字符串的情况
在这里插入图片描述
不过这样子的话所有的Long类型都变成字符串类型,但是有些不用精度丢失的字段没必要,下面第二种方法可以解决这个问题
2.消息转换器不变,在前面那个过滤器中加上逻辑判断
在这里插入图片描述

再次测试发现格式转换和脱敏都成功了
在这里插入图片描述
源码地址在https://gitee.com/fuiyue/desensitization

对于fastjson消息转换器中SerializerFeature属性参考Fastjson SerializerFeature详解,可以根据需求设置一些属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值