背景:
技术采用的是 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