场景1:查询出的字段页面需要脱敏显示
解决方式如下
1、通过sql实现字段脱敏
select CONCAT(LEFT(token, 6), '***', RIGHT(token, 6)) as token from device;
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;
}
}
看下请求返回结果
3、通过MapStruct转换
@Mapping(expression = "java(cn.hutool.core.util.DesensitizedUtil.idCardNum(deviceRespInfoVO.getToken(),3,3))",target = "token")
DeviceRespVO respInfo2vo(DeviceRespInfoVO deviceRespInfoVO);
转换结果同上
场景2:需要注意的是查询接口显示脱敏后,更新接口传入的数据就是脱敏后的数据,所以更新的时候需要过滤这种情况
解决方法如下:
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;
}
}
使用时直接在需要检测的字段上添加注解即可
小结
本文介绍了两种查询数据脱敏的方式,以及避免更新接口会更新脱敏字段覆盖数据库原有字段的方式,数据脱敏的方法还有很多,比如自定义注解实现动态脱敏,可以自行了解