實現方案2種
1.Springboot參數攔截
繼承AbstractNamedValueMethodArgumentResolver
2.Springboot AOP 所有String類型的參數提交都會被驗證,替換規則參考百度
@Around實現
package com.bysk.admin.common.filter; import com.bysk.base.util.RedisUtils; import lombok.SneakyThrows; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * @author: zhangyb * @date: 2020/11/19 9:21 * @Description: 敏感詞過濾 只會攔截使用了Post和put請求的方法即修改和新增,感覺會影響性能, * 后面建議使用單獨注解實現關鍵字過濾 * @UpdateUser : zhangyb * @UpdateDate :2020/11/19 9:21 * @UpdateRemark: */ @Aspect @Component public class SensitiveWord { @Autowired RedisUtils redisUtils; @Autowired WordFilter wordFilter; @Around("@annotation(org.springframework.web.bind.annotation.PostMapping)||@annotation(org.springframework.web.bind.annotation.PutMapping)") @SneakyThrows public Object doBefore(ProceedingJoinPoint point) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); if (request.getRequestURI().contains("/word/sensitiveword")) { return point.proceed(); } //所有的參數對象 for (Object arg : point.getArgs()) { //參數對象,通過反射將String類型的值進行敏感詞過濾 Class<?> aClass = arg.getClass(); //遞歸遍歷,將所有String參數進行敏感詞匹配 foundString(aClass,arg); } return point.proceed(); } /** * @author: zhangyb * @date: 2020/11/19 13:57 * @Description: 遞歸將String替換 * @UpdateUser : zhangyb * @UpdateDate :2020/11/19 13:57 * @UpdateRemark: */ @SneakyThrows public Class<?> foundString(Class clazz,Object arg ){ Field[] declaredFields = clazz.getDeclaredFields(); for (Field declaredField : declaredFields) { Class<?> type = declaredField.getType(); if (type==String.class&&!Modifier.toString(declaredField.getModifiers()).contains("final")){ //如果是String類型,進行關鍵詞匹配 且要排除final修飾的字段 declaredField.setAccessible(true); String value=(String)declaredField.get(arg); declaredField.set(arg, wordFilter.replaceWords(value)); }else if (type.getPackage()!=null&&type.getPackage().getName().contains("com.bysk")){ Method[] methods = clazz.getMethods(); for (Method method : methods) { String name = method.getName(); if (name.toLowerCase().contains("get"+declaredField.getName().toLowerCase())){ Object invoke = method.invoke(arg); this.foundString(type,invoke); break; } } } } return clazz; } }
package com.bysk.admin.common.filter; import com.bysk.admin.modules.word.mapper.SensitiveWordMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.*; /** * @Author: zhangyb * @Date: 2020/11/19 11:13 */ /** * @program: 農事雲 * @description: * @Author: Zhangyb * @CreateDate: 11:13 * @UpdateUser: * @UpdateDate * @UpdateRemark: * @Version: 1.0 */ @Slf4j @Component public class WordFilter { private final static String WORDS = "WORDS"; private final static String REPLACE_CHAR = "*"; private static HashMap sensitiveWordMap; /** * 最小匹配規則 */ private static int minMatchTYpe = 1; /** * 最大匹配規則 */ private static int maxMatchType = 2; @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private SensitiveWordMapper sensitiveWordMapper ; public String replaceWords(String text) { if (StringUtils.isBlank(text)) { return text; } List<Object> words = redisTemplate.opsForList().range("SensitiveWord", 0, -1); if (words.size()<=0){ words=sensitiveWordMapper.listStr(); //更新redis redisTemplate.opsForList().leftPushAll("SensitiveWord",sensitiveWordMapper.listStr()); } //緩存獲取敏感詞匯原記錄 return WordFilter.replaceSensitiveWord(words, text, WordFilter.minMatchTYpe); } /** * 替換敏感字字符 * * @param data 敏感字集合 * @param txt 待檢查文本 * @param matchType 匹配規則 */ private static String replaceSensitiveWord(List<Object> data, String txt, int matchType) { if (sensitiveWordMap == null) { addSensitiveWord(data); } String resultTxt = txt; //獲取所有的敏感詞 List<String> set = getSensitiveWord(txt, matchType); Iterator<String> iterator = set.iterator(); while (iterator.hasNext()) { resultTxt = resultTxt.replaceAll(iterator.next(), REPLACE_CHAR); } return resultTxt; } /** * 讀取敏感詞庫,將敏感詞放入HashSet中,構建一個DFA算法模型: * 說明:該方法來源於互聯網 */ private static void addSensitiveWord(List<Object> datas) { sensitiveWordMap = new HashMap(datas.size()); Iterator<Object> iterator = datas.iterator(); Map<String, Object> now = null; Map now2 = null; while (iterator.hasNext()) { now2 = sensitiveWordMap; String word = (String)iterator.next(); //敏感詞 word=word.trim(); for (int i = 0; i < word.length(); i++) { char key_word = word.charAt(i); Object obj = now2.get(key_word); if (obj != null) { //存在 now2 = (Map) obj; } else { //不存在 now = new HashMap<String, Object>(); now.put("isEnd", "0"); now2.put(key_word, now); now2 = now; } if (i == word.length() - 1) { now2.put("isEnd", "1"); } } } } /** * 獲取內容中的敏感詞 *說明:該方法來源於互聯網 * @param text 內容 * @param matchType 匹配規則 1=不最佳匹配,2=最佳匹配 * @return */ private static List<String> getSensitiveWord(String text, int matchType) { List<String> words = new ArrayList<String>(); Map now = sensitiveWordMap; int count = 0; //初始化敏感詞長度 int start = 0; //標志敏感詞開始的下標 for (int i = 0; i < text.length(); i++) { char key = text.charAt(i); now = (Map) now.get(key); if (now != null) { //存在 count++; if (count == 1) { start = i; } if ("1".equals(now.get("isEnd"))) { //敏感詞結束 now = sensitiveWordMap; //重新獲取敏感詞庫 words.add(text.substring(start, start + count)); //取出敏感詞,添加到集合 count = 0; //初始化敏感詞長度 } } else { //不存在 now = sensitiveWordMap;//重新獲取敏感詞庫 if (count == 1 && matchType == 1) { //不最佳匹配 count = 0; } else if (count == 1 && matchType == 2) { //最佳匹配 words.add(text.substring(start, start + count)); count = 0; } } } return words; } public WordFilter() { super(); } }