Spring Boot 实现敏感词及特殊字符过滤处理
背景:
技术采用的是 Spring Boot ,请求方法主要为 POST, 请求使用较多的注解为 @RequestBody
交付测试人员进行测试,测试人员在对模糊搜索模块进行了各种特殊字符的搜索,以至于敏感词和特殊字符均会入库。
对于我这样有情怀的开发者而言,是不能容忍的。
上来就是干!主要采用
@ControllerAdvice(basePackages = "com.my")
的方式,对用户提交的数据做处理。
以下是示例代码,不影响笔者要言表的功能实现:
/** * @author Ryan * @date 2019/4/25 18:41 */ @ControllerAdvice(basePackages = "com.ytkj") public class EscapeSensitiveWordFilter implements RequestBodyAdvice { @Override public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { return true; } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { return inputMessage; } @Override public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { if(o != null){ SensitiveWordUtils.apply(o); } return o; } @Override public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { return o; } }
由于我们主要针对提交的数据做处理,主要入口在 SensitiveWordUtils.apply(o); 这里的 “Object ” 参数,其实也就是我们 Controller 方法参数中,打了 @RequestBody 的实体。我们可以直接在这里,使用一些手段做处理即可。
这里的手段,也只能使用反射了(如果读者有什么好的方案可以告诉我)。
1. 字符串替换;
2. 自定义抛出运行时异常;
这样做的另外一个好处就是,可以在这里统一管理敏感词。
如果你使用 replaceAll 的话,统一管理上就比较费劲了。
最后,笔者把自己写的反射放在下面,仅供参考,敏感词替换部分写了一个“测试“ 作为要替换入口的标记。
欢迎各界大佬来扶正!
import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; /** * @author Ryan * @date 2019/4/26 12:40 */ public class SensitiveWordUtils { /** * @param result * @return */ public static Object apply(Object result) { if (result == null) { return null; } objectParse(result); return result; } /** * @param obj */ public static void objectParse(Object obj) { List<Field> allField = findAllField(obj); for (Field field : allField) { field.setAccessible(true); Class<?> typeClazz = field.getType(); matchFieldType(obj, field, typeClazz); } } public static List<Field> findAllField(Object object){ List<Field> result = new ArrayList<>(); Class<?> clazz = object.getClass(); while (true) { clazz = clazz.getSuperclass(); if (clazz == Object.class) { break; } Field[] declaredFields = clazz.getDeclaredFields(); result.addAll(Arrays.asList(declaredFields)); } return result; } /** * @param obj * @param field * @param clazz */ public static <T> void matchFieldType(Object obj, Field field, T clazz) { try { T param = (T) field.get(obj); if(param == null){ return; } if (clazz == List.class) { List p = (List)param; for (Object o : p) { objectParse(o); } } else if (clazz == String.class) { setValue(obj, field, "测试"); } else if (clazz == Map.class) { Map map = (Map)param; for (Object o : map.keySet()) { objectParse(o); } } } catch (IllegalAccessException e) { e.printStackTrace(); } } /** * * @param object * @param field * @param param * @throws IllegalAccessException */ public static void setValue(Object object, Field field, Object param) throws IllegalAccessException { if(!field.isAccessible()){ throw new IllegalAccessException("modify the field fail."); } field.set(object, param); } }
这里的 SensitiveWordUtils 还有很大的优化点,我在这里没有目前只是看看效果,写的很粗糙,望大神不要喷。
读者自行实现一下,我说一下优化点:
1. 缓存 object 的 String.class 类型的 Field 或者 methodName; 在第一次加载的时候,缓存进去;放到 ConcurrentHashMap<ObjectType, List<StringField>> , 是不是感觉清爽了好多;
2. 过滤出来 String 类型的 Field ,其他的类型酌情考虑;
3. 等臣妾的再想想;
Spring Boot 统一敏感词过滤 demo
对象序列化前的处理
例如springframework框架(responseBody)json 格式:
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyAdviceChain#beforeBodyWrite
中进行对象数据的转换。
@ControllerAdvice @Slf4j public class ShanDongShengYuHandler implements ResponseBodyAdvice { @Autowired private ObjectMapper objectMapper; @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) { ResponseData d = new ResponseData(); sensitiveHidden(body); d.setData(body); return d; } /** * 只支持自定义类型数据的敏感词过滤,考虑递归性能 */ private void sensitiveHidden(Object body) { if(body==null || StringUtils.isBlank(body.getClass().getName()) || !body.getClass().getName().contains("山东")){ return; } Field[] declaredFields = body.getClass().getDeclaredFields(); for (Field declaredField : declaredFields) { SensitiveWorldHidden annotation = declaredField.getAnnotation(SensitiveWorldHidden.class); log.warn("【注解类型】{}",annotation); try { declaredField.setAccessible(true); Object o = declaredField.get(body); if(annotation != null) { String content = objectMapper.writeValueAsString(o); content = content.replace("垃圾", "**"); Object replaced = objectMapper.readValue(content, o.getClass()); declaredField.set(body, replaced); }else { sensitiveHidden(o); } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (JsonProcessingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。
相关文章
解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题
这篇文章主要介绍了解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-28- 这篇文章主要介绍了cmd下过滤文件名称的两种方法,需要的朋友可以参考下...2020-06-30
- 这篇文章主要为大家详细介绍了SpringBoot实现excel文件生成和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-09
- 这篇文章主要介绍了详解springBoot启动时找不到或无法加载主类解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
- 这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-10
- 这篇文章主要介绍了解决Springboot get请求是参数过长的情况,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-17
Spring Boot项目@RestController使用重定向redirect方式
这篇文章主要介绍了Spring Boot项目@RestController使用重定向redirect方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-02- 这篇文章主要介绍了Springboot+TCP监听服务器搭建过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-10-28
- 这篇文章主要介绍了springBoot 项目排除数据库启动方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-10
springboot中使用@Transactional注解事物不生效的坑
这篇文章主要介绍了springboot中使用@Transactional注解事物不生效的原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-26- 这篇文章主要介绍了SpringBoot接口接收json参数解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-19
详解SpringBoot之访问静态资源(webapp...)
这篇文章主要介绍了详解SpringBoot之访问静态资源(webapp...),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-14- 这篇文章主要介绍了springboot多模块包扫描问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用详解
这篇文章主要介绍了Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-11-18- 这篇文章主要介绍了Springboot实现多线程注入bean的工具类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-27
Springboot+MDC+traceId日志中打印唯一traceId
本文主要介绍了Springboot+MDC+traceId日志中打印唯一traceId,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-17SpringBoot部署到Linux读取resources下的文件及遇到的坑
本文主要给大家介绍SpringBoot部署到Linux读取resources下的文件,在平时业务开发过程中,很多朋友在获取到文件内容乱码或者文件读取不到的问题,今天给大家分享小编遇到的坑及处理方案,感兴趣的朋友跟随小编一起看看吧...2021-06-21- 这篇文章主要介绍了springboot中nacos动态路由的配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-11
SpringBoot高版本修改为低版本时测试类报错的解决方案
这篇文章主要介绍了SpringBoot高版本修改为低版本时测试类报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-18解决Springboot整合shiro时静态资源被拦截的问题
这篇文章主要介绍了解决Springboot整合shiro时静态资源被拦截的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-26