Spring Boot 實現(xiàn)敏感詞及特殊字符過濾處理
背景:
技術(shù)采用的是 Spring Boot ,請求方法主要為 POST, 請求使用較多的注解為 @RequestBody
交付測試人員進(jìn)行測試,測試人員在對模糊搜索模塊進(jìn)行了各種特殊字符的搜索,以至于敏感詞和特殊字符均會入庫。
對于我這樣有情懷的開發(fā)者而言,是不能容忍的。
上來就是干!主要采用
@ControllerAdvice(basePackages = "com.my")
的方式,對用戶提交的數(shù)據(jù)做處理。
以下是示例代碼,不影響筆者要言表的功能實現(xiàn):
/**
* @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;
}
}
由于我們主要針對提交的數(shù)據(jù)做處理,主要入口在 SensitiveWordUtils.apply(o); 這里的 “Object ” 參數(shù),其實也就是我們 Controller 方法參數(shù)中,打了 @RequestBody 的實體。我們可以直接在這里,使用一些手段做處理即可。
這里的手段,也只能使用反射了(如果讀者有什么好的方案可以告訴我)。
1. 字符串替換;
2. 自定義拋出運行時異常;
這樣做的另外一個好處就是,可以在這里統(tǒng)一管理敏感詞。
如果你使用 replaceAll 的話,統(tǒng)一管理上就比較費勁了。
最后,筆者把自己寫的反射放在下面,僅供參考,敏感詞替換部分寫了一個“測試“ 作為要替換入口的標(biāo)記。
歡迎各界大佬來扶正!
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 還有很大的優(yōu)化點,我在這里沒有目前只是看看效果,寫的很粗糙,望大神不要噴。
讀者自行實現(xiàn)一下,我說一下優(yōu)化點:
1. 緩存 object 的 String.class 類型的 Field 或者 methodName; 在第一次加載的時候,緩存進(jìn)去;放到 ConcurrentHashMap<ObjectType, List<StringField>> , 是不是感覺清爽了好多;
2. 過濾出來 String 類型的 Field ,其他的類型酌情考慮;
3. 等臣妾的再想想;
Spring Boot 統(tǒng)一敏感詞過濾 demo
對象序列化前的處理
例如springframework框架(responseBody)json 格式:
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyAdviceChain#beforeBodyWrite
中進(jìn)行對象數(shù)據(jù)的轉(zhuǎn)換。
@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;
}
/**
* 只支持自定義類型數(shù)據(jù)的敏感詞過濾,考慮遞歸性能
*/
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();
}
}
}
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java的可變參數(shù)與Collections類的功能示例解析
這篇文章主要為大家介紹了Java的可變參數(shù)與Collections類的功能示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
使用Java7的Files工具類和Path接口來訪問文件的方法
下面小編就為大家分享一篇使用Java7的Files工具類和Path接口來訪問文件的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-11-11

