spring boot 使用DFA算法实现敏感词过滤
敏感词、文字过滤是一个网站必不可少的功能,如何设计一个好的、高效的过滤算法是非常有必要的。
DFA算法简介
DFA全称为:Deterministic Finite Automaton,即确定有穷自动机。其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号。
确定:状态以及引起状态转换的事件都是可确定的,不存在“意外”。有穷:状态以及事件的数量都是可穷举的。
计算机操作系统中的进程状态与切换可以作为 DFA 算法的一种近似理解。如下图所示,其中椭圆表示状态,状态之间的连线表示事件,进程的状态以及事件都是可确定的,且都可以穷举。
数据结构
state_event_dict
= {
"匹": {
"配": {
"算": {
"法": {
"is_end": True
},
"is_end": False
},
"关": {
"键": {
"词": {
"is_end": True
},
"is_end": False
},
"is_end": False
},
"is_end": False
},
"is_end": False
},
"信": {
"息": {
"抽": {
"取": {
"is_end": True
},
"is_end": False
},
"is_end": False
},
"is_end": False
}
}
Java实现DFA算法实现敏感词过滤
定义敏感词词库 让SpringBoot加载到
这里用到了 @Bean 以及 @Autowired 注入到算法map里面
@Configuration
public class CheckConfig {
@Autowired
private DFAUtil dfaUtil
;
@Bean
public void init(){
Set
<String> set
=new HashSet<>();
set
.add("你是");
set
.add("我是");
set
.add("他是");
set
.add("弟弟");
set
.add("大家好");
dfaUtil
.createDFAHashMap(set
);
}
}
创建AOP切面 前置通知
package com
.yxl
.aspect
;
@Component
@Aspect
@Slf4j
public class CheckInputParameterAspect {
private static final Log Logger
= LogFactory
.getLog(com
.yxzapp
.aspect
.CheckInputParameterAspect
.class);
@Autowired
private DFAUtil dfaUtil
;
@Pointcut("execution(public * com.yxl.modules.*..*Controller.*(..))")
public void params() {
}
@Before("params()")
public Object
before(JoinPoint point
) throws Throwable
{
ServletRequestAttributes attributes
= (ServletRequestAttributes
) RequestContextHolder
.getRequestAttributes();
HttpServletRequest request
= Objects
.requireNonNull(attributes
).getRequest();
Object
[] args
= point
.getArgs();
Set
<String> sensitiveWordByDFAMap
= dfaUtil
.getSensitiveWordByDFAMap(StringUtils
.join(args
,","), 1);
Logger
.info("敏感词有"+sensitiveWordByDFAMap
.size()+"个");
if(sensitiveWordByDFAMap
.size()>=1){
throw new BizException("500","您输入的内容有敏感词");
}
Logger
.info("当前调用接口-[" + request
.getRequestURL() + "]");
return args
;
}
}
DFA算法 工具类
package com
.yxzapp
.utils
;
import org
.springframework
.stereotype
.Component
;
import java
.util
.HashMap
;
import java
.util
.HashSet
;
import java
.util
.Set
;
@Component
public class DFAUtil {
HashMap
<String, Object> dfaMap
;
public static final int minMatchType
=1;
public static final int maxMatchType
=2;
public void createDFAHashMap(Set
<String> set
){
HashMap
<String, Object> nowMap
;
dfaMap
=new HashMap<>(set
.size());
for(String key
:set
){
nowMap
=dfaMap
;
for(int i
=0;i
<key
.length();i
++){
String nowChar
=String
.valueOf(key
.charAt(i
));
HashMap
<String, Object> map
=(HashMap
<String, Object>)nowMap
.get(nowChar
);
if(map
==null
){
map
=new HashMap<String,Object>();
nowMap
.put(nowChar
, map
);
}
nowMap
=map
;
if(nowMap
.containsKey("isEnd")&&nowMap
.get("isEnd").equals("1")){
continue;
}
if(i
!=key
.length()-1){
nowMap
.put("isEnd", "0");
}
else{
nowMap
.put("isEnd", "1");
}
}
}
System
.out
.println(dfaMap
);
}
public Set
<String> getSensitiveWordByDFAMap(String string
,int matchType
){
Set
<String> set
=new HashSet<>();
for(int i
=0;i
<string
.length();i
++){
int length
=getSensitiveLengthByDFAMap(string
,i
,matchType
);
if(length
>0){
set
.add(string
.substring(i
,i
+length
));
}
}
return set
;
}
public int getSensitiveLengthByDFAMap(String string
,int beginIndex
,int matchType
){
int nowLength
=0;
int resultLength
=0;
HashMap
<String, Object> nowMap
=dfaMap
;
for(int i
=beginIndex
;i
<string
.length();i
++){
String nowChar
=String
.valueOf(string
.charAt(i
));
nowMap
=(HashMap
<String, Object>)nowMap
.get(nowChar
);
if(nowMap
==null
){
break;
}
else{
nowLength
++;
if("1".equals(nowMap
.get("isEnd"))){
resultLength
=nowLength
;
if(matchType
==minMatchType
){
break;
}
}
}
}
return resultLength
;
}
}
测试
@ApiOperation(value
= "测试", notes
= "测试")
@PostMapping("gettest")
public ResultBody
gettest(@RequestParam String aa
,
@RequestParam String bb
){
return ResultBody
.success(aa
+bb
);
}
有更好方式 或者 优化的小伙伴可以评论
个人博客: http://blog.yanxiaolong.cn/.