好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

背景:

技术采用的是 Spring Boot ,请求方法主要为 POST, 请求使用较多的注解为 @RequestBody

交付测试人员进行测试,测试人员在对模糊搜索模块进行了各种特殊字符的搜索,以至于敏感词和特殊字符均会入库。

对于我这样有情怀的开发者而言,是不能容忍的。

上来就是干!主要采用

?

1

@ControllerAdvice (basePackages = "com.my" )

的方式,对用户提交的数据做处理。

以下是示例代码,不影响笔者要言表的功能实现:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

/**

  * @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 的话,统一管理上就比较费劲了。

最后,笔者把自己写的反射放在下面,仅供参考,敏感词替换部分写了一个[测试[ 作为要替换入口的标记。

欢迎各界大佬来扶正!

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

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 格式:

?

1

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyAdviceChain#beforeBodyWrite

中进行对象数据的转换。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

@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();

             }

         }

     }

}

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

原文链接:https://my.oschina.net/Rayn/blog/3042780

查看更多关于Spring Boot 实现敏感词及特殊字符过滤处理的详细内容...

  阅读:51次