Spring Boot 实现敏感词及特殊字符过滤处理


Posted in Java/Android onJune 29, 2021

背景:

技术采用的是 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();
            }
        }
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Java/Android 相关文章推荐
Java常用工具类汇总 附示例代码
Jun 26 Java/Android
分析ZooKeeper分布式锁的实现
Jun 30 Java/Android
Springboot配置suffix指定mvc视图的后缀方法
Jul 03 Java/Android
在Spring-Boot中如何使用@Value注解注入集合类
Aug 02 Java/Android
深入解读Java三大集合之map list set的用法
Nov 11 Java/Android
Spring this调用当前类方法无法拦截的示例代码
Mar 20 Java/Android
SpringBoot整合minio快速入门教程(代码示例)
Apr 03 Java/Android
Java 常见的限流算法详细分析并实现
Apr 07 Java/Android
Qt数据库应用之实现图片转pdf
Jun 01 Java/Android
SpringCloud超详细讲解Feign声明式服务调用
Jun 21 Java/Android
Android基础入门之dataBinding的简单使用教程
Jun 21 Java/Android
SpringBoot详解整合Redis缓存方法
Jul 15 Java/Android
elasticSearch-api的具体操作步骤讲解
Java SSH 秘钥连接mysql数据库的方法
一篇文章带你复习java知识点
Java elasticsearch安装以及部署教程
Java 数组内置函数toArray详解
Java集成swagger文档组件
死磕 java同步系列之synchronized解析
Jun 28 #Java/Android
You might like
destoon调用discuz论坛中带图片帖子的实现方法
2014/08/21 PHP
php获取CSS文件中图片地址并下载到本地的方法
2014/12/02 PHP
如何在Web页面上直接打开、编辑、创建Office文档
2007/03/12 Javascript
javascript 广告后加载,加载完页面再加载广告
2010/11/25 Javascript
jQuery 追加元素的方法如append、prepend、before
2014/01/16 Javascript
用Move.js配合创建CSS3动画的入门指引
2015/07/22 Javascript
js+html5实现的自由落体运动效果代码
2016/01/28 Javascript
基于Jquery插件实现跨域异步上传文件功能
2016/04/26 Javascript
JS获取input file绝对路径的方法(推荐)
2016/08/02 Javascript
基于JavaScript实现拖动滑块效果
2017/02/16 Javascript
js鼠标跟随运动效果
2017/03/11 Javascript
JS字符串按逗号和回车分隔的方法
2017/04/25 Javascript
JavaScript原型继承_动力节点Java学院整理
2017/06/30 Javascript
关于webpack2和模块打包的新手指南(小结)
2017/08/07 Javascript
ES6 javascript的异步操作实例详解
2017/10/30 Javascript
Vue 2.5.2下axios + express 本地请求404的解决方法
2018/02/21 Javascript
javascript填充默认头像方法
2018/02/22 Javascript
create-react-app修改为多页面支持的方法
2018/05/17 Javascript
vue 项目中使用Loading组件的示例代码
2018/08/31 Javascript
node.JS事件机制与events事件模块的使用方法详解
2020/02/06 Javascript
浅谈使用nodejs搭建web服务器的过程
2020/07/20 NodeJs
[04:00]DOTA2解说界神雕侠侣 CJ第四天谷子现场过生日
2013/07/30 DOTA
[01:04:20]完美世界DOTA2联赛PWL S2 LBZS vs Forest 第一场 11.29
2020/12/02 DOTA
python在windows下实现备份程序实例
2014/07/04 Python
python实现查询苹果手机维修进度
2015/03/16 Python
总结用Pdb库调试Python的方式及常用的命令
2016/08/18 Python
python如何实现代码检查
2019/06/28 Python
详解将Python程序(.py)转换为Windows可执行文件(.exe)
2019/07/19 Python
python BlockingScheduler定时任务及其他方式的实现
2019/09/19 Python
Python3 shelve对象持久存储原理详解
2020/03/23 Python
python爬虫分布式获取数据的实例方法
2020/11/26 Python
澳洲国民品牌乡村路折扣店:Country Road & Trenery Outlet
2018/04/19 全球购物
模具设计与制造专业推荐信
2014/02/16 职场文书
信息专业学生学习的自我评价
2014/02/17 职场文书
自查自纠工作总结
2014/10/15 职场文书
家长会感言
2015/08/01 职场文书