java查询字段脱敏

场景1:查询出的字段页面需要脱敏显示

image.png

解决方式如下

1、通过sql实现字段脱敏

select CONCAT(LEFT(token, 6), '***', RIGHT(token, 6))  as token from device;

sql脱敏.png

2、通过ResponseBodyAdvice对@RestController返回值重写
借助的是AOP思想,对返回值进行一次修改,重写ResponseBodyAdvice接口这种方法适用于统一对请求前后进行一些操作的场景,比如说对数据的加解密等
这里脱敏采用的是hutool的公共方法(DesensitizedUtil)
信息脱敏工具-DesensitizedUtil参考链接

@Slf4j
@ControllerAdvice(basePackages = "xxx.application.controller")
public class DesensitizationFilter implements ResponseBodyAdvice {


    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        try {
            Method method = (Method) ((MethodParameter) returnType).getMethod();
            switch (method.getName()) {
                case "searchDeviceInfo":
                    return searchDeviceInfo(body);
                default:
                    return body;
            }
        } catch (Throwable e) {
            log.info("返回对象转换出错, body = ", body, e);
            return body;
        }
    }
private Object searchDeviceInfo(Object body){
        Result<PageInfo<DeviceRespVO>> result = (Result<PageInfo<DeviceRespVO>>) body;
        List<DeviceRespVO> list = result.getData().getList();
        for(DeviceRespVO deviceRespVO :list){      
deviceRespVO.setToken(DesensitizedUtil.idCardNum(deviceRespVO.getToken(),3,3));
        }

        return result;
    }
}

看下请求返回结果

image.png

3、通过MapStruct转换

  @Mapping(expression = "java(cn.hutool.core.util.DesensitizedUtil.idCardNum(deviceRespInfoVO.getToken(),3,3))",target = "token")
  DeviceRespVO respInfo2vo(DeviceRespInfoVO deviceRespInfoVO);

转换结果同上


场景2:需要注意的是查询接口显示脱敏后,更新接口传入的数据就是脱敏后的数据,所以更新的时候需要过滤这种情况

image.png

解决方法如下:
1、通过MapStruct转换
自己写个过滤的方法,在MapStruct转换的时候转换下

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = TypeConvertor.class)
public interface DeviceInfoMapStruct {

    @Mapping(expression = "java(xxx..tokenCheck(deviceUpdateDTO.getToken()))",target = "token")
    DeviceUpdateInfoDTO dto2InfoDto(DeviceUpdateDTO deviceUpdateDTO);
}

    /**
     * 自己写了一个简单的转化方法
     * @param token
     * @return
     */
public class MapStructUtils {

    public static String tokenCheck(String token){
        if(token != null && token.contains("*")){
            return null;
        }
        return token;
    }
}
更新的sql需要判空处理

2、通过ResponseBodyAdvice对@RestController返回值重写
新增注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DesensitizedReqField {
}

重写ResponseBodyAdvice方法,获取注解中字段进行判断

@Slf4j
@ControllerAdvice(basePackages = "xxx.application.controller")
public class RequestRewriteFilter implements RequestBodyAdvice {

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        if (targetType.getTypeName().contains("DeviceUpdateDTO")) {
            return true;
        }
        return false;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        return inputMessage;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        try {
            if (targetType.getTypeName().contains("DeviceUpdateDTO")) {
                DeviceUpdateDTO DeviceUpdateDTO = (DeviceUpdateDTO) body;
                Field[] fields = ReflectUtil.getFields(DeviceUpdateDTO.class);
                for (Field field : fields) {
                    DesensitizedReqField desensitizationReqValue = field.getAnnotation(DesensitizedReqField.class);
                    if (String.class.equals(field.getType()) && desensitizationReqValue != null) {
                        String value = (String) ReflectUtil.getFieldValue(DeviceUpdateDTO, field);
                        // 如果有DesensitizationReqValue注解,并且内容包括*,则替换为null
                        if (value != null && value.contains("*")) {
                            ReflectUtil.setFieldValue(DeviceUpdateDTO, field, null);
                        }
                    }

                }
                return DeviceUpdateDTO;
            }

        } catch (Throwable e) {
            log.warn("请求转换异常", e);
        }

        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }
}

使用时直接在需要检测的字段上添加注解即可


小结
本文介绍了两种查询数据脱敏的方式,以及避免更新接口会更新脱敏字段覆盖数据库原有字段的方式,数据脱敏的方法还有很多,比如自定义注解实现动态脱敏,可以自行了解


参考链接
1、RequestBodyAdvice用法详解-参数加解密示例
2、java自定义注解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。