简单的动态带特殊符号敏感词校验
敏感词之前进行了简单了解,使用结巴分词自带词库可以实现,具体参考我的如下博文
此次在此基础进行了部分优化,优化过程本人简单记录一下,具体优化改造步骤如下所示
1.需求
我们公司需要添加一个敏感词功能,之前进行了简单的调研,使用结巴分词,然后加载自定义分词,即可实现。最近发现公司的敏感词总是需要修改,所以要求实现一个可以随时修改的动态敏感词功能,并且敏感词需要支持常用标点符号的分词,要求既加即用,所以我在原有基础上进行了简单修改,实现如下。
2. 具体实现
主要的简化实现步骤如下:
-
添加结巴分词的pom信息,以及对应的初始化字典信息准备
-
读取配置文件,进行数据信息的初始化,添加字典标点符号支持,加载数据字典信息
项目字典加载顺序是首先从本地resource目录下获取/dict/custom.dict
文件中获取敏感词信息,如果数据库没有初始化,则加载信息到数据库中,如果数据加载完毕则跳过此步骤。之后则生成一个临时文件,添加敏感词数据字典信息设定词频,然后将此文件内容加载到结巴分词的词库中,最后删除文件
custom.dict 文件格式如下:
-
数据字典的增删改查,当数据字典有变动之后,需要重新加载数据字典
-
服务为微服务,当修改单节点服务时,需要通知其他节点服务,字典同步更新
项目的整体目录结构如下所示:
2.1 添加pom
我们使用的spring boot 项目,新增pom信息如下:
<!-- 敏感词的包 -->
<dependency>
<groupId>com.huaban</groupId>
<artifactId>jieba-analysis</artifactId>
<version>1.0.2</version>
</dependency>
2.2 项目初始化加载字典
-
新增对应的字典表结构
DROP TABLE MANAGE.TB_SYS_SENSITIVE_WORDS --TbSysSensitiveWords CREATE TABLE MANAGE.TB_SYS_SENSITIVE_WORDS ( ID varchar(32), SENSITIVE_WORD varchar(100), SENSITIVE_EXCHANGE_WORD varchar(100), WORD_FREQUENCY number, WORD_DESC varchar(200), ctime date DEFAULT sysdate, mtime date DEFAULT sysdate, is_del varchar(1) DEFAULT 0, primary key(ID) ) COMMENT ON COLUMN MANAGE.TB_SYS_SENSITIVE_WORDS.ID IS '主键'; COMMENT ON COLUMN MANAGE.TB_SYS_SENSITIVE_WORDS.SENSITIVE_WORD IS '敏感词'; COMMENT ON COLUMN MANAGE.TB_SYS_SENSITIVE_WORDS.SENSITIVE_EXCHANGE_WORD IS '特殊符号转义后的敏感词'; COMMENT ON COLUMN MANAGE.TB_SYS_SENSITIVE_WORDS.WORD_FREQUENCY IS '词频,注意如果敏感词不好用,可以加大词频,使其生效,默认50'; COMMENT ON COLUMN MANAGE.TB_SYS_SENSITIVE_WORDS.WORD_DESC IS '词语备注描述'; COMMENT ON TABLE MANAGE.TB_SYS_SENSITIVE_WORDS IS '敏感词词库表';
-
新增初始化执行方法
package cn.git.manage.init; import cn.git.manage.util.CommonAnalyzerUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /** * @description: 数据字典初始化 * @program: bank-credit-sy * @author: lixuchun * @create: 2024-12-03 */ @Component public class CommonAnalyzerInit { @Autowired private CommonAnalyzerUtil analyzerUtil; /** * 初始化加载自定义分词词典 * * idea测试环境可用,linux分词加载自定义字典,需要读取jar包中文件内容,spring boot 打包运行后,无法直接读取,获取Path对象 * 所以复制一份临时文件到本地,再加载 */ @PostConstruct public void init() { analyzerUtil.analyzerInit(); } }
-
新增 CommonAnalyzerUtil 工具类
具体的加载字典以及特殊标点符号的转义都在此方法中package cn.git.manage.util; import cn.git.common.exception.ServiceException; import cn.git.common.util.LogUtil; import cn.git.common.util.ServerIpUtil; import cn.git.elk.util.NetUtil; import cn.git.manage.entity.TbSysSensitiveWords; import cn.git.manage.mapper.TbSysSensitiveWordsMapper; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import com.huaban.analysis.jieba.WordDictionary; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; /** * @description: 数据字典初通用方法 * @program: bank-credit-sy * @author: lixuchun * @create: 2024-12-03 */ @Slf4j @Component public class CommonAnalyzerUtil { @Autowired private TbSysSensitiveWordsMapper sensitiveWordsMapper; /** * 敏感词集合 */ public static Set<String> SENSITIVE_WORDS_SET = new HashSet<>(); /** * 自定义词典路径 */ private static final String DICT_PATH = "/dict/custom.dict"; /** * 临时文件名称 */ private static final String TEMP_FILE_NAME = "custom_tmp.dict"; /** * 系统标识win系统 */ private static final String WINDOWS_SYS = "windows"; /** * 系统标识属性 */ private static final String OS_FLAG = "os.name"; /** * 当前项目路径 */ private static final String USER_DIR = "user.dir"; @Autowired private ServerIpUtil serverIpUtil; @Autowired private SqlSessionFactory sqlSessionFactory; /** * 特殊符号集合 */ private static final Set<Character> SPECIAL_SYMBOLS = new HashSet<>(); /** * 敏感词替换以及还原MAP */ private static final Map<Character, String> REPLACEMENTS = new HashMap<>(); private static final Map<String, Character> REVERSE_REPLACEMENTS = new HashMap<>(); /** * 静态代码块,初始化特殊符号集合 */ static { // 敏感词特殊符号集合 SPECIAL_SYMBOLS.add('|'); SPECIAL_SYMBOLS.add('?'); SPECIAL_SYMBOLS.add('#'); SPECIAL_SYMBOLS.add('?'); SPECIAL_SYMBOLS.add('*'); SPECIAL_SYMBOLS.add('$'); SPECIAL_SYMBOLS.add('^'); SPECIAL_SYMBOLS.add('&'); SPECIAL_SYMBOLS.add('('); SPECIAL_SYMBOLS.add(')'); SPECIAL_SYMBOLS.add('('); SPECIAL_SYMBOLS.add(')'); SPECIAL_SYMBOLS.add('{'); SPECIAL_SYMBOLS.add('}'); SPECIAL_SYMBOLS.add('【'); SPECIAL_SYMBOLS.add('】'); SPECIAL_SYMBOLS.add('['); SPECIAL_SYMBOLS.add(']'); SPECIAL_SYMBOLS.add('"'); SPECIAL_SYMBOLS.add('\''); SPECIAL_SYMBOLS.add(';'); SPECIAL_SYMBOLS.add(':'); SPECIAL_SYMBOLS.add('!'); SPECIAL_SYMBOLS.add('!'); SPECIAL_SYMBOLS.add(','); SPECIAL_SYMBOLS.add(','); SPECIAL_SYMBOLS.add('.'); SPECIAL_SYMBOLS.add('<'); SPECIAL_SYMBOLS.add('>'); SPECIAL_SYMBOLS.add('《'); SPECIAL_SYMBOLS.add('》'); SPECIAL_SYMBOLS.add('%'); SPECIAL_SYMBOLS.add('@'); SPECIAL_SYMBOLS.add('~'); SPECIAL_SYMBOLS.add('='); SPECIAL_SYMBOLS.add('_'); SPECIAL_SYMBOLS.add(' '); SPECIAL_SYMBOLS.add('\\'); SPECIAL_SYMBOLS.add('+'); SPECIAL_SYMBOLS.add('-'); SPECIAL_SYMBOLS.add('/'); // 敏感词替换以及还原MAP初始化 REPLACEMENTS.put('|', "竖线"); REPLACEMENTS.put('?', "问号"); REPLACEMENTS.put('?', "中文问号"); REPLACEMENTS.put('!', "中文感叹号"); REPLACEMENTS.put('!', "感叹号"); REPLACEMENTS.put('*', "星号"); REPLACEMENTS.put('$', "美元"); REPLACEMENTS.put('^', "尖号"); REPLACEMENTS.put('\\', "反斜线"); REPLACEMENTS.put('/', "斜线"); REPLACEMENTS.put('&', "与"); REPLACEMENTS.put('(', "中文左括号"); REPLACEMENTS.put(')', "中文右括号"); REPLACEMENTS.put('(', "左括号"); REPLACEMENTS.put(')', "右括号"); REPLACEMENTS.put('{', "左大括号"); REPLACEMENTS.put('}', "右大括号"); REPLACEMENTS.put('【', "中文左中括号"); REPLACEMENTS.put('】', "中文右中括号"); REPLACEMENTS.put('[', "左中括号"); REPLACEMENTS.put(']', "右中括号"); REPLACEMENTS.put('"', "双引号"); REPLACEMENTS.put('\'', "单引号"); REPLACEMENTS.put(';', "分号")