前言
这里的项目实战, 我们使用的是 SpringBoot2.x+JDK1.8搭建的,核心思想是借助了Hutool工具类的 WordTree。想了解更多DFA算法的实现可以参考DFA算法的实现
实战案例
1. 引入Hutool的工具类
xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
</dependency>
2. 自定义铭感词过滤处理器
Hutool工具类中定义了 SensitiveProcessor 接口,它的作用是把敏感词替换成 *
java
package cn.hutool.dfa;
/**
* @author 肖海斌
* 敏感词过滤处理器,默认按字符数替换成*
*/
public interface SensitiveProcessor {
/**
* 敏感词过滤处理
* @param foundWord 敏感词匹配到的内容
* @return 敏感词过滤后的内容,默认按字符数替换成*
*/
default String process(FoundWord foundWord) {
int length = foundWord.getFoundWord().length();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
sb.append("*");
}
return sb.toString();
}
}
我们可以根据不同的业务需求,实现不同的处理器。这里可以定义了一个默认处理器 和高亮处理器。
SensitiveDefaultProcessor 默认处理器和原逻辑一样,可以直接调用父类的process()方法实现把铭感词替换为*
java
import cn.hutool.dfa.FoundWord;
import cn.hutool.dfa.SensitiveProcessor;
/**
* 自定义敏感词*号替代处理器
*/
public class SensitiveDefaultProcessor implements SensitiveProcessor {
}
SensitiveHighlightProcessor 定义了敏感词进行高亮处理,可以在铭感词前后打上对应的标签。
java
import cn.hutool.dfa.FoundWord;
import cn.hutool.dfa.SensitiveProcessor;
/**
* 自定义敏感词高亮处理器
*/
public class SensitiveHighlightProcessor implements SensitiveProcessor {
private static final String SHIELD_START = "<shield>";
private static final String SHIELD_END = "</shield>";
private static final String DST_START = "<dst>";
private static final String DST_END = "</dst>";
private static final String WARN_START = "<warn>";
private static final String WARN_END = "</warn>";
@Override
public String process(FoundWord foundWord) {
String word = foundWord.getFoundWord();
StringBuilder sb = new StringBuilder();
sb.append(WARN_START).append(word).append(WARN_END);
return sb.toString();
}
public String process(FoundWord foundWord, SensitiveWordModeEnum mode) {
String word = foundWord.getFoundWord();
StringBuilder sb = new StringBuilder();
if (SensitiveWordModeEnum.SHIELD.equals(mode)) {
sb.append(SHIELD_START).append(word).append(SHIELD_END);
} else if (SensitiveWordModeEnum.DST.equals(mode)) {
sb.append(DST_START).append(word).append(DST_END);
} else if (SensitiveWordModeEnum.WARN.equals(mode)) {
sb.append(WARN_START).append(word).append(WARN_END);
}
return sb.toString();
}
}
其中 SensitiveWordModeEnum 是自己定义的一个敏感词模式枚举
java
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 敏感词模式枚举
*
*/
@AllArgsConstructor
@Getter
public enum SensitiveWordModeEnum {
SHIELD("SHIELD", "屏蔽"),
DST("DST", "脱敏"),
WARN("WARN", "警告");
@EnumValue
@JsonValue
private final String code;
private final String name;
}
3. 定义铭感词初始化工具
通常来说,铭感词的内容是相对固定的。我们在项目启动时可以进行预加载。当铭感词变更时,我们可以通过更新本地缓存,定时刷新的方法进行处理。
Tips: 我们可以定义初始化类,它实现了ApplicationRunner 接口。这个类中的 run方法将会在Boot项目的程序的入口方法 main 执行完毕之后被调用。在该类中可以定义一些应用程序启动后需要进行初始化的操作 或任务
java
/**
* 敏感词工具类
*/
@Slf4j
@Component
public class SensitiveWordUtil implements ApplicationRunner {
// 一个定义铭感词查找的Dao层 [查询数据库中的敏感词数据]
@Resource
private SensitiveWordConfigDao sensitiveWordConfigDao;
// DFA敏感词树
private static final WordTree SENSITIVE_TREE = new WordTree();
// 定义了一个初始化的敏感词容器
private static final ConcurrentHashMap<String, SensitiveWordConfigVO> SENSITIVE_WORDS_MAP = new ConcurrentHashMap<>();
@Override
public void run(ApplicationArguments args) {
// 1.查询数据库中的铭感词列表
List<SensitiveWordConfig> sensitiveWordConfigList = sensitiveWordConfigDao.list();
if (ObjectUtil.isEmpty(sensitiveWordConfigList)) {
return;
}
for (SensitiveWordConfig sensitiveWord : sensitiveWordConfigList) {
// 1.1 敏感词VO对象的转换
SensitiveWordConfigVO sensitiveWordConfigVO = new SensitiveWordConfigVO();
BeanUtils.copyProperties(sensitiveWord, sensitiveWordConfigVO);
//1.2 本地容器缓存的初始化
SENSITIVE_WORDS_MAP.put(sensitiveWordConfigVO.getWord(), sensitiveWordConfigVO);
}
// 1.3 初始DFA敏感词树
this.init(ListUtil.toList(SENSITIVE_WORDS_MAP.keys()), true);
log.info("初始化敏感词库完毕, 共" + sensitiveWordConfigList.size() + "个敏感词");
}
/**
* 初始化敏感词树
* @param isAsync 是否异步初始化
* @param sensitiveWords 敏感词列表
*/
public void init(final Collection<String> sensitiveWords, boolean isAsync) {
if (isAsync) {
ThreadUtil.execAsync(() -> {
init(sensitiveWords);
return true;
});
} else {
init(sensitiveWords);
}
}
/**
* 初始化敏感词树
*
* @param sensitiveWords 敏感词列表
*/
public void init(Collection<String> sensitiveWords) {
SENSITIVE_TREE.clear();
SENSITIVE_TREE.addWords(sensitiveWords);
}
}
上面的工具类调用 run() 方法后,就能实现铭感词容器的初始化 。
除了定义一些最基础的初始化步骤外,我们可以把一些添加铭感词,移除敏感词,查找等方法都定义在该类中。
java
// 添加敏感词
public static void addSensitiveWord(SensitiveWordConfig sw) {
SensitiveWordConfigVO vo = new SensitiveWordConfigVO();
BeanUtils.copyProperties(sensitiveWord, vo);
SENSITIVE_WORDS_MAP.put(sw.getWord(), vo);
SENSITIVE_TREE.addWord(sw.getWord());
}
// 移除敏感词
public static void removeSensitiveWord(String word) {
SENSITIVE_WORDS_MAP.remove(word);
SENSITIVE_TREE.clear();
SENSITIVE_TREE.addWords(ListUtil.toList(SENSITIVE_WORDS_MAP.keySet()));
}
/**
* 查找敏感词,返回找到的第一个敏感词
*
* @param text 文本
* @return 敏感词
* @since 5.5.3
*/
public static FoundWord getFoundFirstSensitive(String text) {
return SENSITIVE_TREE.matchWord(text);
}
// 还可以通过上面的processor处理器进行敏感词处理
至此,我们在项目中只要引入SensitiveWordUtil 工具类,就能实现敏感词的基本操作了。