8000 GitHub - wwwisme/sensitive-word: 👮â€â™‚ï¸The sensitive word tool for java.(æ•æ„Ÿè¯/è¿ç¦è¯/è¿æ³•è¯/è„è¯ã€‚基于 DFA 算法实现的高性能 java æ•æ„Ÿè¯è¿‡æ»¤å·¥å…·æ¡†æž¶ã€‚内置支æŒå•è¯æ ‡ç­¾åˆ†ç±»åˆ†çº§ã€‚请勿å‘å¸ƒæ¶‰åŠæ”¿æ²»ã€å¹¿å‘Šã€è¥é”€ã€ç¿»å¢™ã€è¿åå›½å®¶æ³•å¾‹æ³•è§„ç­‰å†…å®¹ã€‚é«˜æ€§èƒ½æ•æ„Ÿè¯æ£€æµ‹è¿‡æ»¤ç»„件,附带ç¹ä½“简体互æ¢ï¼Œæ”¯æŒå…¨è§’åŠè§’互æ¢ï¼Œæ±‰å­—转拼音,模糊æœç´¢ç­‰åŠŸèƒ½ã€‚)
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

👮â€â™‚ï¸The sensitive word tool for java.(æ•æ„Ÿè¯/è¿ç¦è¯/è¿æ³•è¯/è„è¯ã€‚基于 DFA 算法实现的高性能 java æ•æ„Ÿè¯è¿‡æ»¤å·¥å…·æ¡†æž¶ã€‚内置支æŒå•è¯æ ‡ç­¾åˆ†ç±»åˆ†çº§ã€‚请勿å‘å¸ƒæ¶‰åŠæ”¿æ²»ã€å¹¿å‘Šã€è¥é”€ã€ç¿»å¢™ã€è¿åå›½å®¶æ³•å¾‹æ³•è§„ç­‰å†…å®¹ã€‚é«˜æ€§èƒ½æ•æ„Ÿè¯æ£€æµ‹è¿‡æ»¤ç»„件,附带ç¹ä½“简体互æ¢ï¼Œæ”¯æŒå…¨è§’åŠè§’互æ¢ï¼Œæ±‰å­—转拼音,模糊æœç´¢ç­‰åŠŸèƒ½ã€‚)

License

Notifications You must be signed in to change notification settings

wwwisme/sensitive-word

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sensitive-word

sensitive-word 基于 DFA ç®—æ³•å®žçŽ°çš„é«˜æ€§èƒ½æ•æ„Ÿè¯å·¥å…·ã€‚

Maven Central Open Source Love

如果有一些疑难æ‚症,å¯ä»¥åŠ å…¥ï¼šæŠ€æœ¯äº¤æµç¾¤

sensitive-word-admin 是对应的控å°çš„应用,目å‰åŠŸèƒ½å¤„äºŽåˆæœŸå¼€å‘中,MVP 版本å¯ç”¨ã€‚

创作目的

大家好,我是è€é©¬ã€‚

一直想实现一款简å•å¥½ç”¨æ•æ„Ÿè¯å·¥å…·ï¼ŒäºŽæ˜¯å¼€æºå®žçŽ°äº†è¿™ä¸ªå·¥å…·ã€‚

基于 DFA ç®—æ³•å®žçŽ°ï¼Œç›®å‰æ•感è¯åº“内容收录 6W+ï¼ˆæºæ–‡ä»¶ 18W+,ç»è¿‡ä¸€æ¬¡åˆ å‡ï¼‰ã€‚

åŽæœŸå°†è¿›è¡ŒæŒç»­ä¼˜åŒ–å’Œè¡¥å……æ•æ„Ÿè¯åº“,并进一步æå‡ç®—法的性能。

v0.24.0 开始内置支æŒå¯¹æ•感è¯çš„分类细化,ä¸è¿‡å·¥ä½œé‡æ¯”较大,难å…å­˜åœ¨ç–æ¼ã€‚

欢迎 PR 改进, github æéœ€æ±‚,或者加入技术交æµç¾¤æ²Ÿé€šå¹ç‰›ï¼

特性

全角åŠè§’互æ¢ã€è‹±æ–‡å¤§å°å†™äº’æ¢ã€æ•°å­—常è§å½¢å¼çš„互æ¢ã€ä¸­æ–‡ç¹ç®€ä½“互æ¢ã€è‹±æ–‡å¸¸è§å½¢å¼çš„互æ¢ã€å¿½ç•¥é‡å¤è¯ç­‰

项目推è

䏋颿˜¯ä¸€äº›æ—¥å¿—ã€åŠ è§£å¯†ã€è„±æ•安全相关的库推è:

项目 介ç»
sensitive-word é«˜æ€§èƒ½æ•æ„Ÿè¯æ ¸å¿ƒåº“
sensitive-word-admin æ•æ„Ÿè¯æŽ§å°ï¼Œå‰åŽç«¯åˆ†ç¦»
sensitive 高性能日志脱æ•组件
auto-log 统一日志切é¢ç»„件,支æŒå…¨é“¾è·¯traceId
encryption-local 离线加密机组件
encryption 加密机标准API+本地客户端
encryption-server 加密机æœåŠ¡

å˜æ›´æ—¥å¿—

CHANGE_LOG.md

更多资料

æ•æ„Ÿè¯æŽ§å°

æœ‰æ—¶å€™æ•æ„Ÿè¯æœ‰ä¸€ä¸ªæŽ§å°ï¼Œé…置起æ¥ä¼šæ›´åŠ çµæ´»æ–¹ä¾¿ã€‚

java 如何实现开箱å³ç”¨çš„æ•æ„Ÿè¯æŽ§å°æœåŠ¡ï¼Ÿ

æ•æ„Ÿè¯æ ‡ç­¾æ–‡ä»¶

梳ç†äº†å¤§é‡çš„æ•æ„Ÿè¯æ ‡ç­¾æ–‡ä»¶ï¼Œå¯ä»¥è®©æˆ‘ä»¬çš„æ•æ„Ÿè¯æ›´åŠ æ–¹ä¾¿ã€‚

这两个资料阅读å¯åœ¨ä¸‹æ–¹æ–‡ç« èŽ·å–:

v0.11.0-æ•æ„Ÿè¯æ–°ç‰¹æ€§åŠå¯¹åº”标签文件

ç›®å‰ v0.24.0 已内置实现å•è¯æ ‡ç­¾ï¼Œéœ€è¦çš„建议å‡çº§åˆ°æœ€æ–°ç‰ˆæœ¬ã€‚

支æŒå¼€æº

å¼€æºä¸æ˜“,如果本项目对你有帮助,你å¯ä»¥è¯·è€é©¬å–一æ¯å¥¶èŒ¶ã€‚

快速开始

准备

  • JDK1.8+

  • Maven 3.x+

Maven 引入

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>sensitive-word</artifactId>
    <version>0.26.0</version>
</dependency>

核心方法

SensitiveWordHelper ä½œä¸ºæ•æ„Ÿè¯çš„工具类,核心方法如下:

注æ„:SensitiveWordHelper æä¾›çš„都是默认é…ç½®ã€‚å¦‚æžœä½ å¸Œæœ›è¿›è¡Œçµæ´»çš„自定义é…置,å¯å‚考 引导类特性é…ç½®

方法 傿•° 返回值 说明
contains(String) 待验è¯çš„字符串 布尔值 验è¯å­—符串是å¦åŒ…嫿•感è¯
replace(String, ISensitiveWordReplace) 使用指定的替æ¢ç­–ç•¥æ›¿æ¢æ•æ„Ÿè¯ å­—ç¬¦ä¸² 返回脱æ•åŽçš„字符串
replace(String, char) 使用指定的 char æ›¿æ¢æ•æ„Ÿè¯ å­—ç¬¦ä¸² 返回脱æ•åŽçš„字符串
replace(String) 使用 * æ›¿æ¢æ•æ„Ÿè¯ å­—ç¬¦ä¸² 返回脱æ•åŽçš„字符串
findAll(String) 待验è¯çš„字符串 字符串列表 è¿”å›žå­—ç¬¦ä¸²ä¸­æ‰€æœ‰æ•æ„Ÿè¯
findFirst(String) 待验è¯çš„字符串 字符串 è¿”å›žå­—ç¬¦ä¸²ä¸­ç¬¬ä¸€ä¸ªæ•æ„Ÿè¯
findAll(String, IWordResultHandler) IWordResultHandler 结果处ç†ç±» 字符串列表 è¿”å›žå­—ç¬¦ä¸²ä¸­æ‰€æœ‰æ•æ„Ÿè¯
findFirst(String, IWordResultHandler) IWordResultHandler 结果处ç†ç±» 字符串 è¿”å›žå­—ç¬¦ä¸²ä¸­ç¬¬ä¸€ä¸ªæ•æ„Ÿè¯
tags(String) èŽ·å–æ•感è¯çš„æ ‡ç­¾ æ•æ„Ÿè¯å­—符串 è¿”å›žæ•æ„Ÿè¯çš„æ ‡ç­¾åˆ—表

判断是å¦åŒ…嫿•感è¯

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";

Assert.assertTrue(SensitiveWordHelper.contains(text));

è¿”å›žç¬¬ä¸€ä¸ªæ•æ„Ÿè¯

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";

String word = SensitiveWordHelper.findFirst(text);
Assert.assertEquals("五星红旗", word);

SensitiveWordHelper.findFirst(text) 等价于:

String word = SensitiveWordHelper.findFirst(text, WordResultHandlers.word());

è¿”å›žæ‰€æœ‰æ•æ„Ÿè¯

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";

List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[五星红旗, 毛主席, 天安门]", wordList.toString());

è¿”å›žæ‰€æœ‰æ•æ„Ÿè¯ç”¨æ³•上类似于 SensitiveWordHelper.findFirst()ï¼ŒåŒæ ·ä¹Ÿæ”¯æŒæŒ‡å®šç»“果处ç†ç±»ã€‚

SensitiveWordHelper.findAll(text) 等价于:

List<String> wordList = SensitiveWordHelper.findAll(text, WordResultHandlers.word());

WordResultHandlers.raw() å¯ä»¥ä¿ç•™å¯¹åº”的下标信æ¯ã€ç±»åˆ«ä¿¡æ¯ï¼š

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";

// é»˜è®¤æ•æ„Ÿè¯æ ‡ç­¾ä¸ºç©º
List<WordTagsDto> wordList1 = SensitiveWordHelper.findAll(text, WordResultHandlers.wordTags());
Assert.assertEquals("[WordTagsDto{word='五星红旗', tags=[]}, WordTagsDto{word='毛主席', tags=[]}, WordTagsDto{word='天安门', tags=[]}]", wordList1.toString());

默认的替æ¢ç­–ç•¥

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";
String result = SensitiveWordHelper.replace(text);
Assert.assertEquals("****迎风飘扬,***的画åƒå±¹ç«‹åœ¨***å‰ã€‚", result);

指定替æ¢çš„内容

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";
String result = SensitiveWordHelper.replace(text, '0');
Assert.assertEquals("0000迎风飘扬,000的画åƒå±¹ç«‹åœ¨000å‰ã€‚", result);

自定义替æ¢ç­–ç•¥

V0.2.0 支æŒè¯¥ç‰¹æ€§ã€‚

场景说明:有时候我们希望ä¸åŒçš„æ•æ„Ÿè¯æœ‰ä¸åŒçš„æ›¿æ¢ç»“æžœã€‚æ¯”å¦‚ã€æ¸¸æˆã€‘替æ¢ä¸ºã€ç”µå­ç«žæŠ€ã€‘,ã€å¤±ä¸šã€‘替æ¢ä¸ºã€çµæ´»å°±ä¸šã€‘。

诚然,æå‰ä½¿ç”¨å­—符串的正则替æ¢ä¹Ÿå¯ä»¥ï¼Œä¸è¿‡æ€§èƒ½ä¸€èˆ¬ã€‚

使用例å­ï¼š

/**
 * 自定替æ¢ç­–ç•¥
 * @since 0.2.0
 */
@Test
public void defineReplaceTest() {
    final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";

    ISensitiveWordReplace replace = new MySensitiveWordReplace();
    String result = SensitiveWordHelper.replace(text, replace);

    Assert.assertEquals("国家旗帜迎风飘扬,教员的画åƒå±¹ç«‹åœ¨***å‰ã€‚", result);
}

其中 MySensitiveWordReplace 是我们自定义的替æ¢ç­–略,实现如下:

public class MyWordReplace implements IWordReplace {

    @Override
    public void replace(StringBuilder stringBuilder, final char[] rawChars, IWordResult wordResult, IWordContext wordContext) {
        String sensitiveWord = InnerWordCharUtils.getString(rawChars, wordResult);
        // 自定义ä¸åŒçš„æ•æ„Ÿè¯æ›¿æ¢ç­–略,å¯ä»¥ä»Žæ•°æ®åº“等地方读å–
        if("五星红旗".equals(sensitiveWord)) {
            stringBuilder.append("国家旗帜");
        } else if("毛主席".equals(sensitiveWord)) {
            stringBuilder.append("教员");
        } else {
            // 其他默认使用 * 代替
            int wordLength = wordResult.endIndex() - wordResult.startIndex();
            for(int i = 0; i < wordLength; i++) {
                stringBuilder.append('*');
            }
        }
    }

}

我们针对其中的部分è¯åšå›ºå®šæ˜ å°„处ç†ï¼Œå…¶ä»–的默认转æ¢ä¸º *。

IWordResultHandler 结果处ç†ç±»

IWordResultHandler å¯ä»¥å¯¹æ•感è¯çš„结果进行处ç†ï¼Œå…许用户自定义。

å†…ç½®å®žçŽ°è§ WordResultHandlers 工具类:

  • WordResultHandlers.word()

åªä¿ç•™æ•感è¯å•è¯æœ¬èº«ã€‚

  • WordResultHandlers.raw()

ä¿ç•™æ•感è¯ç›¸å…³ä¿¡æ¯ï¼ŒåŒ…嫿•感è¯çš„开始和结æŸä¸‹æ ‡ã€‚

  • WordResultHandlers.wordTags()

åŒæ—¶ä¿ç•™å•è¯ï¼Œå’Œå¯¹åº”çš„è¯æ ‡ç­¾ä¿¡æ¯ã€‚

使用实例

所有测试案例å‚è§ SensitiveWordHelperTest

1)基本例å­

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";

List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[五星红旗, 毛主席, 天安门]", wordList.toString());
List<String> wordList2 = SensitiveWordHelper.findAll(text, WordResultHandlers.word());
Assert.assertEquals("[五星红旗, 毛主席, 天安门]", wordList2.toString());

List<IWordResult> wordList3 = SensitiveWordHelper.findAll(text, WordResultHandlers.raw());
Assert.assertEquals("[WordResult{startIndex=0, endIndex=4}, WordResult{startIndex=9, endIndex=12}, WordResult{startIndex=18, endIndex=21}]", wordList3.toString());
  1. wordTags 例å­

我们在 dict_tag_test.txt 文件中指定对应è¯çš„æ ‡ç­¾ä¿¡æ¯ã€‚

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";

// é»˜è®¤æ•æ„Ÿè¯æ ‡ç­¾ä¸ºç©º
List<WordTagsDto> wordList1 = SensitiveWordHelper.findAll(text, WordResultHandlers.wordTags());
Assert.assertEquals("[WordTagsDto{word='五星红旗', tags=[]}, WordTagsDto{word='毛主席', tags=[]}, WordTagsDto{word='天安门', tags=[]}]", wordList1.toString());

List<WordTagsDto> wordList2 = SensitiveWordBs.newInstance()
        .wordTag(WordTags.file("dict_tag_test.txt"))
        .init()
        .findAll(text, WordResultHandlers.wordTags());
Assert.assertEquals("[WordTagsDto{word='五星红旗', tags=[政治, 国家]}, WordTagsDto{word='毛主席', tags=[政治, 伟人, 国家]}, WordTagsDto{word='天安门', tags=[政治, 国家, 地å€]}]", wordList2.toString());

更多特性

åŽç»­çš„è¯¸å¤šç‰¹æ€§ï¼Œä¸»è¦æ˜¯é’ˆå¯¹å„ç§é’ˆå¯¹å„ç§æƒ…况的处ç†ï¼Œå°½å¯èƒ½çš„æå‡æ•感è¯å‘½ä¸­çŽ‡ã€‚

这是一场漫长的攻防之战。

æ ·å¼å¤„ç†

忽略大å°å†™

final String text = "fuCK the bad words.";

String word = SensitiveWordHelper.findFirst(text);
Assert.assertEquals("fuCK", word);

忽略åŠè§’圆角

final String text = "fuck the bad words.";

String word = SensitiveWordHelper.findFirst(text);
Assert.assertEquals("fuck", word);

忽略数字的写法

这里实现了数字常è§å½¢å¼çš„转æ¢ã€‚

final String text = "这个是我的微信:9⓿二肆â¹â‚ˆâ‘¢â‘¸â’‹âžƒãˆ¤ãŠ„";

List<String> wordList = SensitiveWordBs.newInstance().enableNumCheck(true).init().findAll(text);
Assert.assertEquals("[9⓿二肆â¹â‚ˆâ‘¢â‘¸â’‹âžƒãˆ¤ãŠ„]", wordList.toString());

忽略ç¹ç®€ä½“

final String text = "我爱我的祖国和五星紅旗。";

List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[五星紅旗]", wordList.toString());

忽略英文的书写格å¼

final String text = "Ⓕⓤc⒦ the bad words";

List<String> wordList = SensitiveWordHelper.findAll(text);
Assert.assertEquals("[Ⓕⓤc⒦]", wordList.toString());

忽略é‡å¤è¯

final String text = "ⒻⒻⒻfⓤuⓤ⒰cⓒ⒦ the bad words";

List<String> wordList = SensitiveWordBs.newInstance()
        .ignoreRepeat(true)
        .init()
        .findAll(text);
Assert.assertEquals("[ⒻⒻⒻfⓤuⓤ⒰cⓒ⒦]", wordList.toString());

更多检测策略

说明

v0.25.0 ç›®å‰çš„几个策略,也支æŒç”¨æˆ·å¼•导类自定义。所有的策略都是接å£ï¼Œæ”¯æŒç”¨æˆ·è‡ªå®šä¹‰å®žçŽ°ã€‚

åºå· 方法 说明 默认值
16 wordCheckNum 数字检测策略(v0.25.0开始支æŒ) WordChecks.num()
17 wordCheckEmail 邮箱检测策略(v0.25.0开始支æŒ) WordChecks.email()
18 wordCheckUrl URL检测策略(v0.25.0开始支æŒ),内置还是实现了 urlNoPrefix() (WordChecks.url()
19 wordCheckIpv4 ipv4检测策略(v0.25.0开始支æŒ) WordChecks.ipv4()
20 wordCheckWord æ•æ„Ÿè¯æ£€æµ‹ç­–ç•¥(v0.25.0开始支æŒ) WordChecks.word()

内置实现:

a) WordChecks.urlNoPrefix() 作为 url çš„é¢å¤–实现,å¯ä»¥ä¸éœ€è¦ https:// å’Œ http:// å‰ç¼€ã€‚

邮箱检测

邮箱等个人信æ¯ï¼Œé»˜è®¤æœªå¯ç”¨ã€‚

final String text = "楼主好人,邮箱 sensitiveword@xx.com";
List<String> wordList = SensitiveWordBs.newInstance().enableEmailCheck(true).init().findAll(text);
Assert.assertEquals("[sensitiveword@xx.com]", wordList.toString());

连续数字检测

一般用于过滤手机å·/QQ等广告信æ¯ï¼Œé»˜è®¤æœªå¯ç”¨ã€‚

V0.2.1 之åŽï¼Œæ”¯æŒé€šè¿‡ numCheckLen(长度) 自定义检测的长度。

final String text = "你懂得:12345678";

// 默认检测 8 ä½
List<String> wordList = SensitiveWordBs.newInstance()
.enableNumCheck(true)
.init().findAll(text);
Assert.assertEquals("[12345678]", wordList.toString());

// 指定数字的长度,é¿å…误æ€
List<String> wordList2 = SensitiveWordBs.newInstance()
.enableNumCheck(true)
.numCheckLen(9)
.init()
.findAll(text);
Assert.assertEquals("[]", wordList2.toString());

ç½‘å€æ£€æµ‹

用于过滤常è§çš„网å€ä¿¡æ¯ï¼Œé»˜è®¤æœªå¯ç”¨ã€‚

v0.18.0 优化 URL 检测,更加严格,é™ä½Žè¯¯åˆ¤çއ

final String text = "点击链接 https://www.baidu.com 查看答案";
final SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance().enableUrlCheck(true).init();
List<String> wordList = sensitiveWordBs.findAll(text);
Assert.assertEquals("[https://www.baidu.com]", wordList.toString());
Assert.assertEquals("点击链接 ********************* 查看答案", sensitiveWordBs.replace(text));

v0.25.0 内置支æŒä¸éœ€è¦ http å议的å‰ç¼€æ£€æµ‹ï¼š

final String text = "点击链接 https://www.baidu.com 查看答案,当然也å¯ä»¥æ˜¯ baidu.comã€www.baidu.com";

final SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance()
        .enableUrlCheck(true) // å¯ç”¨URL检测
        .wordCheckUrl(WordChecks.urlNoPrefix()) //指定检测的方å¼
        .init();
List<String> wordList = sensitiveWordBs.findAll(text);
Assert.assertEquals("[www.baidu.com, baidu.com, www.baidu.com]", wordList.toString());

Assert.assertEquals("点击链接 https://************* 查看答案,当然也å¯ä»¥æ˜¯ *********ã€*************", sensitiveWordBs.replace(text));

IPV4 检测

v0.17.0 支æŒ

é¿å…用户通过 ip ç»•è¿‡ç½‘å€æ£€æµ‹ç­‰ï¼Œé»˜è®¤æœªå¯ç”¨ã€‚

final String text = "ä¸ªäººç½‘ç«™ï¼Œå¦‚æžœç½‘å€æ‰“ä¸å¼€å¯ä»¥è®¿é—® 127.0.0.1。";
final SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance().enableIpv4Check(true).init();
List<String> wordList = sensitiveWordBs.findAll(text);
Assert.assertEquals("[127.0.0.1]", wordList.toString());

引导类特性é…ç½®

说明

上é¢çš„特性默认都是开å¯çš„,有时业务需è¦çµæ´»å®šä¹‰ç›¸å…³çš„é…置特性。

所以 v0.0.14 开放了属性é…置。

é…置方法

为了让使用更加优雅,统一使用 fluent-api 的方å¼å®šä¹‰ã€‚

用户å¯ä»¥ä½¿ç”¨ SensitiveWordBs 进行如下定义:

注æ„:é…ç½®åŽï¼Œè¦ä½¿ç”¨æˆ‘们新定义的 SensitiveWordBs çš„å¯¹è±¡ï¼Œè€Œä¸æ˜¯ä»¥å‰çš„工具方法。工具方法é…置都是默认的。

SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
        .ignoreCase(true)
        .ignoreWidth(true)
        .ignoreNumStyle(true)
        .ignoreChineseStyle(true)
        .ignoreEnglishStyle(true)
        .ignoreRepeat(false)
        .enableNumCheck(false)
        .enableEmailCheck(false)
        .enableUrlCheck(false)
        .enableIpv4Check(false)
        .enableWordCheck(true)
        .wordFailFast(true)
        .wordCheckNum(WordChecks.num())
        .wordCheckEmail(WordChecks.email())
        .wordCheckUrl(WordChecks.url())
        .wordCheckIpv4(WordChecks.ipv4())
        .wordCheckWord(WordChecks.word())
        .numCheckLen(8)
        .wordTag(WordTags.none())
        .charIgnore(SensitiveWordCharIgnores.defaults())
        .wordResultCondition(WordResultConditions.alwaysTrue())
        .init();

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";
Assert.assertTrue(wordBs.contains(text));

é…置说明

其中å„项é…置的说明如下:

åºå· 方法 说明 默认值
1 ignoreCase 忽略大å°å†™ true
2 ignoreWidth 忽略åŠè§’圆角 true
3 ignoreNumStyle 忽略数字的写法 true
4 ignoreChineseStyle å¿½ç•¥ä¸­æ–‡çš„ä¹¦å†™æ ¼å¼ true
5 ignoreEnglishStyle å¿½ç•¥è‹±æ–‡çš„ä¹¦å†™æ ¼å¼ true
6 ignoreRepeat 忽略é‡å¤è¯ false
7 enableNumCheck 是å¦å¯ç”¨æ•°å­—检测。 false
8 enableEmailCheck 是有å¯ç”¨é‚®ç®±æ£€æµ‹ false
9 enableUrlCheck 是å¦å¯ç”¨é“¾æŽ¥æ£€æµ‹ false
10 enableIpv4Check 是å¦å¯ç”¨IPv4检测 false
11 enableWordCheck 是å¦å¯ç”¨æ•感å•è¯æ£€æµ‹ true
12 numCheckLen 数字检测,自定义指定长度。 8
13 wordTag è¯å¯¹åº”的标签 none
14 charIgnore 忽略的字符 none
15 wordResultCondition 针对匹é…çš„æ•æ„Ÿè¯é¢å¤–加工,比如å¯ä»¥é™åˆ¶è‹±æ–‡å•è¯å¿…é¡»å…¨åŒ¹é… æ’为真
16 wordCheckNum 数字检测策略(v0.25.0开始支æŒ) WordChecks.num()
17 wordCheckEmail 邮箱检测策略(v0.25.0开始支æŒ) WordChecks.email()
18 wordCheckUrl URL检测策略(v0.25.0开始支æŒ) (WordChecks.url()
19 wordCheckIpv4 ipv4检测策略(v0.25.0开始支æŒ) WordChecks.ipv4()
20 wordCheckWord æ•æ„Ÿè¯æ£€æµ‹ç­–ç•¥(v0.25.0开始支æŒ) WordChecks.word()
21 wordReplace 替æ¢ç­–ç•¥ WordReplaces.defaults()
22 wordFailFast æ•æ„Ÿè¯åŒ¹é…æ¨¡å¼æ˜¯å¦å¿«é€Ÿè¿”回 true

wordFailFast æ•æ„Ÿè¯åŒ¹é…快速失败模å¼

场景说明

v0.26.0 开始支æŒã€‚

默认情况下,wordFailFast=trueã€‚åŒ¹é…æ—¶å¿«é€Ÿè¿”回,性能较好。

但是有时候ä¸å¤ªç¬¦åˆäººçš„直觉。

默认如下:

SensitiveWordBs bs2 = SensitiveWordBs.newInstance()
        .wordDeny(new IWordDeny() {
            @Override
            public List<String> deny() {
                return Arrays.asList("我的世界", "我的");
            }
        }).init();
String text = "ä»–çš„ä¸–ç•Œå®ƒçš„ä¸–ç•Œå’Œå¥¹çš„ä¸–ç•Œéƒ½ä¸æ˜¯æˆ‘çš„ä¹Ÿä¸æ˜¯æˆ‘的世界";
List<String> textList2 = bs2.findAll(text);
Assert.assertEquals(Arrays.asList("我的", "我的"), textList2);

此时会优先匹é…çŸ­çš„ã€æˆ‘的】,导致åŽé¢çš„ã€æˆ‘的世界】被跳过。

failOver 模å¼

å°½å¯èƒ½æ‰¾åˆ°æœ€é•¿çš„匹é…è¯ã€‚

SensitiveWordBs bs = SensitiveWordBs.newInstance()
        .wordFailFast(false)
        .wordDeny(new IWordDeny() {
            @Override
            public List<String> deny() {
                return Arrays.asList("我的世界", "我的");
            }
        }).init();

String text = "ä»–çš„ä¸–ç•Œå®ƒçš„ä¸–ç•Œå’Œå¥¹çš„ä¸–ç•Œéƒ½ä¸æ˜¯æˆ‘çš„ä¹Ÿä¸æ˜¯æˆ‘的世界";
List<String> textList = bs.findAll(text);
Assert.assertEquals(Arrays.asList("我的", "我的世界"), textList);

内存资æºçš„释放

v0.16.1 开始支æŒï¼Œæœ‰æ—¶å€™æˆ‘们需è¦é‡Šæ”¾å†…存,å¯ä»¥å¦‚下:

关于内存回收问题

SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
                .init();
// åŽç»­å› ä¸ºä¸€äº›åŽŸå› ç§»é™¤äº†å¯¹åº”ä¿¡æ¯ï¼Œå¸Œæœ›é‡Šæ”¾å†…存。
wordBs.destroy();

针对å•个黑åå•è¯çš„æ–°å¢ž/删除,无需全é‡åˆå§‹åŒ–

使用场景:在åˆå§‹åŒ–之åŽï¼Œæˆ‘们希望针对å•个è¯çš„æ–°å¢ž/åˆ é™¤ï¼Œè€Œä¸æ˜¯å®Œå…¨é‡æ–°åˆå§‹åŒ–。这个特性就是为此准备的。

支æŒç‰ˆæœ¬ï¼šv0.19.0

方法说明

addWord(word) æ–°å¢žæ•æ„Ÿè¯ï¼Œæ”¯æŒå•个è¯/集åˆ

removeWord(word) åˆ é™¤æ•æ„Ÿè¯ï¼Œæ”¯æŒå•个è¯/集åˆ

实例代ç ï¼š

final String text = "æµ‹è¯•ä¸€ä¸‹æ–°å¢žæ•æ„Ÿè¯ï¼ŒéªŒè¯ä¸€ä¸‹åˆ é™¤å’Œæ–°å¢žå¯¹ä¸å¯¹";

SensitiveWordBs sensitiveWordBs =
SensitiveWordBs.newInstance()
        .wordAllow(WordAllows.empty())
        .wordDeny(WordDenys.empty())
        .init();

// 当å‰
Assert.assertEquals("[]", sensitiveWordBs.findAll(text).toString());

// 新增å•个
sensitiveWordBs.addWord("测试");
sensitiveWordBs.addWord("新增");
Assert.assertEquals("[测试, 新增, 新增]", sensitiveWordBs.findAll(text).toString());

// 删除å•个
sensitiveWordBs.removeWord("新增");
Assert.assertEquals("[测试]", sensitiveWordBs.findAll(text).toString());
sensitiveWordBs.removeWord("测试");
Assert.assertEquals("[]", sensitiveWordBs.findAll(text).toString());

// 新增集åˆ
sensitiveWordBs.addWord(Arrays.asList("新增", "测试"));
Assert.assertEquals("[测试, 新增, 新增]", sensitiveWordBs.findAll(text).toString());
// 删除集åˆ
sensitiveWordBs.removeWord(Arrays.asList("新增", "测试"));
Assert.assertEquals("[]", sensitiveWordBs.findAll(text).toString());

// 新增数组
sensitiveWordBs.addWord("新增", "测试");
Assert.assertEquals("[测试, 新增, 新增]", sensitiveWordBs.findAll(text).toString());
// 删除集åˆ
sensitiveWordBs.removeWord("新增", "测试");
Assert.assertEquals("[]", sensitiveWordBs.findAll(text).toString());

针对å•个白åå•è¯çš„æ–°å¢ž/删除,无需全é‡åˆå§‹åŒ–

使用场景:在åˆå§‹åŒ–之åŽï¼Œæˆ‘们希望针对å•个è¯çš„æ–°å¢ž/åˆ é™¤ï¼Œè€Œä¸æ˜¯å®Œå…¨é‡æ–°åˆå§‹åŒ–。这个特性就是为此准备的。

支æŒç‰ˆæœ¬ï¼šv0.21.0

方法说明

addWordAllow(word) 新增白åå•,支æŒå•个è¯/集åˆ

removeWordAllow(word) 删除白åå•,支æŒå•个è¯/集åˆ

使用例å­

        final String text = "æµ‹è¯•ä¸€ä¸‹æ–°å¢žæ•æ„Ÿè¯ç™½åå•,验è¯ä¸€ä¸‹åˆ é™¤å’Œæ–°å¢žå¯¹ä¸å¯¹";

        SensitiveWordBs sensitiveWordBs =
                SensitiveWordBs.newInstance()
                        .wordAllow(WordAllows.empty())
                        .wordDeny(new IWordDeny() {
                            @Override
                            public List<String> deny() {
                                return Arrays.asList("测试", "新增");
                            }
                        })
                        .init();

        // 当å‰
        Assert.assertEquals("[测试, 新增, 新增]", sensitiveWordBs.findAll(text).toString());

        // 新增å•个
        sensitiveWordBs.addWordAllow("测试");
        sensitiveWordBs.addWordAllow("新增");
        Assert.assertEquals("[]", sensitiveWordBs.findAll(text).toString());

        // 删除å•个
        sensitiveWordBs.removeWordAllow("测试");
        Assert.assertEquals("[测试]", sensitiveWordBs.findAll(text).toString());
        sensitiveWordBs.removeWordAllow("新增");
        Assert.assertEquals("[测试, 新增, 新增]", sensitiveWordBs.findAll(text).toString());

        // 新增集åˆ
        sensitiveWordBs.addWordAllow(Arrays.asList("新增", "测试"));
        Assert.assertEquals("[]", sensitiveWordBs.findAll(text).toString());
        // 删除集åˆ
        sensitiveWordBs.removeWordAllow(Arrays.asList("新增", "测试"));
        Assert.assertEquals("[测试, 新增, 新增]", sensitiveWordBs.findAll(text).toString());

        // 新增数组
        sensitiveWordBs.addWordAllow("新增", "测试");
        Assert.assertEquals("[]", sensitiveWordBs.findAll(text).toString());
        // 删除集åˆ
        sensitiveWordBs.removeWordAllow("新增", "测试");
        Assert.assertEquals("[测试, 新增, 新增]", sensitiveWordBs.findAll(text).toString());

å…¨é‡åˆå§‹åŒ–

说明

此方å¼å·²åºŸå¼ƒï¼Œå»ºè®®ä½¿ç”¨ä¸Šé¢å¢žé‡æ·»åŠ çš„æ–¹å¼ï¼Œé¿å…å…¨é‡åŠ è½½ã€‚ä¸ºäº†å…¼å®¹ï¼Œæ­¤æ–¹å¼ä¾ç„¶ä¿ç•™ã€‚

使用方å¼ï¼šåœ¨è°ƒç”¨ sensitiveWordBs.init() çš„æ—¶å€™ï¼Œæ ¹æ® IWordDeny+IWordAllow 釿–°æž„å»ºæ•æ„Ÿè¯åº“。 因为åˆå§‹åŒ–å¯èƒ½è€—时较长(秒级别),所有优化为 init æœªå®Œæˆæ—¶ä¸å½±å“æ—§çš„è¯åº“功能,完æˆåŽä»¥æ–°çš„为准。

例å­

@Component
public class SensitiveWordService {

    @Autowired
    private SensitiveWordBs sensitiveWordBs;

    /**
     * æ›´æ–°è¯åº“
     *
     * æ¯æ¬¡æ•°æ®åº“的信æ¯å‘生å˜åŒ–之åŽï¼Œé¦–先调用更新数æ®åº“æ•æ„Ÿè¯åº“的方法。
     * 如果需è¦ç”Ÿæ•ˆï¼Œåˆ™è°ƒç”¨è¿™ä¸ªæ–¹æ³•。
     *
     * è¯´æ˜Žï¼šé‡æ–°åˆå§‹åŒ–ä¸å½±å“旧的方法使用。åˆå§‹åŒ–完æˆåŽï¼Œä¼šä»¥æ–°çš„为准。
     */
    public void refresh() {
        // æ¯æ¬¡æ•°æ®åº“的信æ¯å‘生å˜åŒ–之åŽï¼Œé¦–先调用更新数æ®åº“æ•æ„Ÿè¯åº“的方法,然åŽè°ƒç”¨è¿™ä¸ªæ–¹æ³•。
        sensitiveWordBs.init();
    }

}

如上,你å¯ä»¥åœ¨æ•°æ®åº“è¯åº“å‘ç”Ÿå˜æ›´æ—¶ï¼Œéœ€è¦è¯åº“生效,主动触å‘一次åˆå§‹åŒ– sensitiveWordBs.init();。

å…¶ä»–ä½¿ç”¨ä¿æŒä¸å˜ï¼Œæ— éœ€é‡å¯åº”用。

wordResultCondition-针对匹é…è¯è¿›ä¸€æ­¥åˆ¤æ–­

说明

支æŒç‰ˆæœ¬ï¼šv0.13.0

有时候我们å¯èƒ½å¸Œæœ›å¯¹åŒ¹é…çš„æ•æ„Ÿè¯è¿›ä¸€æ­¥é™åˆ¶ï¼Œæ¯”如虽然我们定义了ã€avã€‘ä½œä¸ºæ•æ„Ÿè¯ï¼Œä½†æ˜¯ä¸å¸Œæœ›ã€have】被匹é…。

å°±å¯ä»¥è‡ªå®šä¹‰å®žçް wordResultCondition 接å£ï¼Œå®žçŽ°è‡ªå·±çš„ç­–ç•¥ã€‚

系统内置的策略在 WordResultConditions#alwaysTrue() æ’为真,WordResultConditions#englishWordMatch() åˆ™è¦æ±‚英文必须全è¯åŒ¹é…。

内置策略

WordResultConditions 工具类å¯ä»¥èŽ·å–匹é…ç­–ç•¥

实现 说明 支æŒç‰ˆæœ¬
alwaysTrue æ’为真
englishWordMatch 英文å•è¯å…¨è¯åŒ¹é… v0.13.0
englishWordNumMatch 英文å•è¯/æ•°å­—å…¨è¯åŒ¹é… v0.20.0
wordTags 满足特定标签的,比如åªå…³æ³¨ã€å¹¿å‘Šã€‘标签 v0.23.0
chains(IWordResultCondition ...conditions) æ”¯æŒæŒ‡å®šå¤šä¸ªæ¡ä»¶ï¼ŒåŒæ—¶æ»¡è¶³ v0.23.0

使用例å­

原始的默认情况:

final String text = "I have a nice day。";

List<String> wordList = SensitiveWordBs.newInstance()
        .wordDeny(new IWordDeny() {
            @Override
            public List<String> deny() {
                return Collections.singletonList("av");
            }
        })
        .wordResultCondition(WordResultConditions.alwaysTrue())
        .init()
        .findAll(text);
Assert.assertEquals("[av]", wordList.toString());

我们å¯ä»¥æŒ‡å®šä¸ºè‹±æ–‡å¿…须全è¯åŒ¹é…。

final String text = "I have a nice day。";

List<String> wordList = SensitiveWordBs.newInstance()
        .wordDeny(new IWordDeny() {
            @Override
            public List<String> deny() {
                return Collections.singletonList("av");
            }
        })
        .wordResultCondition(WordResultConditions.englishWordMatch())
        .init()
        .findAll(text);
Assert.assertEquals("[]", wordList.toString());

当然å¯ä»¥æ ¹æ®éœ€è¦å®žçŽ°æ›´åŠ å¤æ‚的策略。

wordTags å•è¯æ ‡ç­¾

支æŒç‰ˆæœ¬ï¼š v0.23.0

我们å¯ä»¥åªè¿”回隶属于æŸä¸€ç§æ ‡ç­¾çš„æ•æ„Ÿè¯ã€‚

æˆ‘ä»¬æŒ‡å®šäº†ä¸¤ä¸ªæ•æ„Ÿè¯ï¼šå•†å“ã€AV

MyWordTag æ˜¯æˆ‘ä»¬å®šä¹‰çš„ä¸€ä¸ªæ•æ„Ÿè¯æ ‡ç­¾å®žçŽ°ï¼š

/**
 * 自定义å•è¯æ ‡ç­¾
 * @since 0.23.0
 */
public class MyWordTag extends AbstractWordTag {

    private static Map<String, Set<String>> dataMap;

    static {
        dataMap = new HashMap<>();
        dataMap.put("商å“", buildSet("广告", "中文"));
        dataMap.put("AV", buildSet("色情", "å•è¯", "英文"));
    }

    private static Set<String> buildSet(String... tags) {
        Set<String> set = new HashSet<>();
        for(String tag : tags) {
            set.add(tag);
        }
        return set;
    }

    @Override
    protected Set<String> doGetTag(String word) {
        return dataMap.get(word);
    }

}

测试用例如下,我们模拟了两个ä¸åŒçš„实现类,æ¯ä¸€ä¸ªå…³æ³¨çš„å•è¯æ ‡ç­¾ä¸åŒã€‚

// åªå…³å¿ƒSE情
SensitiveWordBs sensitiveWordBsYellow = SensitiveWordBs.newInstance()
        .wordDeny(new IWordDeny() {
            @Override
            public List<String> deny() {
                return Arrays.asList("商å“", "AV");
            }
        })
        .wordAllow(WordAllows.empty())
        .wordTag(new MyWordTag())
        .wordResultCondition(WordResultConditions.wordTags(Arrays.asList("色情")))
        .init();

// åªå…³å¿ƒå¹¿å‘Š
SensitiveWordBs sensitiveWordBsAd = SensitiveWordBs.newInstance()
        .wordDeny(new IWordDeny() {
            @Override
            public List<String> deny() {
                return Arrays.asList("商å“", "AV");
            }
        })
        .wordAllow(WordAllows.empty())
        .wordTag(new MyWordTag())
        .wordResultCondition(WordResultConditions.wordTags(Arrays.asList("广告")))
        .init();

final String text = "这些 AV 商å“什么价格?";
Assert.assertEquals("[AV]", sensitiveWordBsYellow.findAll(text).toString());
Assert.assertEquals("[商å“]", sensitiveWordBsAd.findAll(text).toString());

忽略字符

说明

æˆ‘ä»¬çš„æ•æ„Ÿè¯ä¸€èˆ¬éƒ½æ˜¯æ¯”较连续的,比如ã€å‚»å¸½ã€‘

é‚£å°±æœ‰å¤§èªæ˜Žå‘现,å¯ä»¥åœ¨ä¸­é—´åŠ ä¸€äº›å­—ç¬¦ï¼Œæ¯”å¦‚ã€å‚»!@#$帽】跳过检测,但是骂人等攻击力ä¸å‡ã€‚

那么,如何应对这些类似的场景呢?

我们å¯ä»¥æŒ‡å®šç‰¹æ®Šå­—符的跳过集åˆï¼Œå¿½ç•¥æŽ‰è¿™äº›æ— æ„义的字符å³å¯ã€‚

v0.11.0 开始支æŒ

例å­

其中 charIgnore 对应的字符策略,用户å¯ä»¥è‡ªè¡Œçµæ´»å®šä¹‰ã€‚

final String text = "傻@冒,狗+东西";

//默认因为有特殊字符分割,无法识别
List<String> wordList = SensitiveWordBs.newInstance().init().findAll(text);
Assert.assertEquals("[]", wordList.toString());

// 指定忽略的字符策略,å¯è‡ªè¡Œå®žçŽ°ã€‚
List<String> wordList2 = SensitiveWordBs.newInstance()
        .charIgnore(SensitiveWordCharIgnores.specialChars())
        .init()
        .findAll(text);

Assert.assertEquals("[傻@冒, 狗+东西]", wordList2.toString());

æ•æ„Ÿè¯æ ‡ç­¾

说明

æœ‰æ—¶å€™æˆ‘ä»¬å¸Œæœ›å¯¹æ•æ„Ÿè¯åŠ ä¸€ä¸ªåˆ†ç±»æ ‡ç­¾ï¼šæ¯”å¦‚ç¤¾æƒ…ã€æš´/力等等。

这样åŽç»­å¯ä»¥æŒ‰ç…§æ ‡ç­¾ç­‰è¿›è¡Œæ›´å¤šç‰¹æ€§æ“作,比如åªå¤„ç†æŸä¸€ç±»çš„æ ‡ç­¾ã€‚

支æŒç‰ˆæœ¬ï¼šv0.10.0

主è¦ç‰¹æ€§æ”¯æŒç‰ˆæœ¬ï¼šv0.24.0

标签接å£

è¿™é‡Œåªæ˜¯ä¸€ä¸ªæŠ½è±¡çš„æŽ¥å£ï¼Œç”¨æˆ·å¯ä»¥è‡ªè¡Œå®šä¹‰å®žçŽ°ã€‚æ¯”å¦‚ä»Žæ•°æ®åº“æŸ¥è¯¢ã€æ–‡ä»¶è¯»å–ã€api 调用等。

public interface IWordTag {

    /**
     * 查询标签列表
     * @param word è„è¯
     * @return 结果
     */
    Set<String> getTag(String word);

}

内置实现

方法列表

为了方便大部分情况使用,内置实现一些场景策略在 WordTags 类中

2851
实现方法 说明 备注
none() 空实现 v0.10.0 支æŒ
file(String filePath) 指定文件路径 v0.10.0 支æŒ
file(String filePath, String wordSplit, String tagSplit) 指定文件路径,以åŠå•è¯åˆ†éš”ç¬¦ã€æ ‡ç­¾åˆ†éš”符 v0.10.0 支æŒ
map(final Map<String, Set> wordTagMap) æ ¹æ® mapåˆå§‹åŒ– v0.24.0 支æŒ
lines(Collection lines) 字符串列表 v0.24.0 支æŒ
lines(Collection lines, String wordSplit, String tagSpli) 字符串列表,以åŠå•è¯åˆ†éš”ç¬¦ã€æ ‡ç­¾åˆ†éš”符 v0.24.0 支æŒ
system() 系件文件内置实现,整åˆç½‘络分类 v0.24.0 支æŒ
defaults() 默认策略,目å‰ä¸º system v0.24.0 支æŒ
chains(IWordTag... others) 链弿–¹æ³•,支æŒç”¨æˆ·æ•´åˆå®žçŽ°å¤šä¸ªç­–ç•¥ v0.24.0 支æŒ

æ ¼å¼çº¦å®š

æ•æ„Ÿè¯æ ‡ç­¾çš„æ ¼å¼æˆ‘们默认约定如下 æ•æ„Ÿè¯ tag1,tag2,代表这 æ•æ„Ÿè¯ 的标签为 tag1 å’Œ tag2

比如

五星红旗 政治,国家

æ‰€æœ‰çš„æ–‡ä»¶è¡Œå†…å®¹ï¼Œå’ŒæŒ‡å®šçš„å­—ç¬¦ä¸²è¡Œå†…å®¹ä¹Ÿå»ºè®®ç”¨è¿™ç§æ–¹å¼ã€‚å¦‚æžœä¸æ»¡è¶³ï¼Œè‡ªå®šä¹‰å®žçްå³å¯ã€‚

系统内置实现(默认效果)

v0.24.0 版本开始,默认的å•è¯æ ‡ç­¾ä¸º WordTags.system()。

è¯´æ˜Žï¼šç›®å‰æ•°æ®ç»Ÿè®¡è‡ªç½‘络,存在ä¸å°‘ç–æ¼ã€‚也欢迎大家指正,æŒç»­æ”¹è¿›ä¸­...

SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance()
.wordTag(WordTags.system())
.init();
Set<String> tagSet = sensitiveWordBs.tags("åšå½©");
Assert.assertEquals("[3]", tagSet.toString());

这里为了压缩大å°ä¼˜åŒ–,对应的类别用数字表示。

æ•°å­—çš„å«ä¹‰åˆ—表如下:

0 政治
1 毒å“
2 色情
3 赌åš
4 è¿æ³•

文件入门例å­

这里以文件为例å­ï¼Œæ¼”示一下如何使用。

final String path = "~\\test\\resources\\dict_tag_test.txt";

// 演示默认方法
IWordTag wordTag = WordTags.file(path);
SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance()
        .wordTag(wordTag)
        .init();

Set<String> tagSet = sensitiveWordBs.tags("é›¶å”®");
        Assert.assertEquals("[广告, 网络]", tagSet.toString());


// 演示指定分隔符
IWordTag wordTag2 = WordTags.file(path, " ", ",");
SensitiveWordBs sensitiveWordBs2 = SensitiveWordBs.newInstance()
        .wordTag(wordTag2)
        .init();
Set<String> tagSet2 = sensitiveWordBs2.tags("é›¶å”®");
        Assert.assertEquals("[广告, 网络]", tagSet2.toString());

其中 dict_tag_test.txt 我们自定义的内容如下:

零售 广告,网络

å•è¯æ ‡ç­¾å’Œæ•感è¯å‘现的è”动

æˆ‘ä»¬åœ¨èŽ·å–æ•感è¯çš„æ—¶å€™ï¼Œæ˜¯å¯ä»¥è®¾ç½®å¯¹åº”的结果处ç†ç­–略,从而获å–å¯¹åº”çš„æ•æ„Ÿè¯æ ‡ç­¾ä¿¡æ¯

// 自定义测试标签类
IWordTag wordTag = WordTags.lines(Arrays.asList("天安门 政治,国家,地å€"));

// 指定åˆå§‹åŒ–
SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance()
        .wordTag(wordTag)
        .init()
        ;

List<WordTagsDto> wordTagsDtoList1 = sensitiveWordBs.findAll("天安门", WordResultHandlers.wordTags());
Assert.assertEquals("[WordTagsDto{word='天安门', tags=[政治, 国家, 地å€]}]", wordTagsDtoList1.toString());

我们自定义了 天安门 关键è¯çš„æ ‡ç­¾ï¼Œç„¶åŽé€šè¿‡æŒ‡å®š findAll 的结果处ç†ç­–略为 WordResultHandlers.wordTags(),就å¯ä»¥åœ¨èŽ·å–æ•感è¯çš„åŒæ—¶ï¼ŒèŽ·å–对应的标签列表。

动æ€åŠ è½½ï¼ˆç”¨æˆ·è‡ªå®šä¹‰ï¼‰

情景说明

æœ‰æ—¶å€™æˆ‘ä»¬å¸Œæœ›å°†æ•æ„Ÿè¯çš„加载设计æˆåЍæ€çš„,比如控å°ä¿®æ”¹ï¼Œç„¶åŽå¯ä»¥å®žæ—¶ç”Ÿæ•ˆã€‚

v0.0.13 支æŒäº†è¿™ç§ç‰¹æ€§ã€‚

接å£è¯´æ˜Ž

为了实现这个特性,并且兼容以å‰çš„功能,我们定义了两个接å£ã€‚

IWordDeny

接å£å¦‚下,å¯ä»¥è‡ªå®šä¹‰è‡ªå·±çš„实现。

è¿”å›žçš„åˆ—è¡¨ï¼Œè¡¨ç¤ºè¿™ä¸ªè¯æ˜¯ä¸€ä¸ªæ•感è¯ã€‚

/**
 * æ‹’ç»å‡ºçŽ°çš„æ•°æ®-è¿”å›žçš„å†…å®¹è¢«å½“åšæ˜¯æ•感è¯
 * @author binbin.hou
 * @since 0.0.13
 */
public interface IWordDeny {

    /**
     * 获å–结果
     * @return 结果
     * @since 0.0.13
     */
    List<String> deny();

}

比如:

public class MyWordDeny implements IWordDeny {

    @Override
    public List<String> deny() {
        return Arrays.asList("æˆ‘çš„è‡ªå®šä¹‰æ•æ„Ÿè¯");
    }

}

IWordAllow

接å£å¦‚下,å¯ä»¥è‡ªå®šä¹‰è‡ªå·±çš„实现。

返回的列表,表示这个è¯ä¸æ˜¯ä¸€ä¸ªæ•感è¯ã€‚

/**
 * å…许的内容-返回的内容ä¸è¢«å½“åšæ•感è¯
 * @author binbin.hou
 * @since 0.0.13
 */
public interface IWordAllow {

    /**
     * 获å–结果
     * @return 结果
     * @since 0.0.13
     */
    List<String> allow();

}

如:

public class MyWordAllow implements IWordAllow {

    @Override
    public List<String> allow() {
        return Arrays.asList("五星红旗");
    }

}

é…置使用

接å£è‡ªå®šä¹‰ä¹‹åŽï¼Œå½“ç„¶éœ€è¦æŒ‡å®šæ‰èƒ½ç”Ÿæ•ˆã€‚

为了让使用更加优雅,我们设计了引导类 SensitiveWordBs。

å¯ä»¥é€šè¿‡ wordDeny() æŒ‡å®šæ•æ„Ÿè¯ï¼ŒwordAllow() æŒ‡å®šéžæ•感è¯ï¼Œé€šè¿‡ init() åˆå§‹åŒ–æ•æ„Ÿè¯å­—典。

系统的默认é…ç½®

SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
        .wordDeny(WordDenys.defaults())
        .wordAllow(WordAllows.defaults())
        .init();

final String text = "五星红旗迎风飘扬,毛主席的画åƒå±¹ç«‹åœ¨å¤©å®‰é—¨å‰ã€‚";
Assert.assertTrue(wordBs.contains(text));

备注:init() å¯¹äºŽæ•æ„Ÿè¯ DFA 的构建是比较耗时的,一般建议在应用åˆå§‹åŒ–的时候åªåˆå§‹åŒ–ä¸€æ¬¡ã€‚è€Œä¸æ˜¯é‡å¤åˆå§‹åŒ–ï¼

指定自己的实现

我们å¯ä»¥æµ‹è¯•一下自定义的实现,如下:

String text = "è¿™æ˜¯ä¸€ä¸ªæµ‹è¯•ï¼Œæˆ‘çš„è‡ªå®šä¹‰æ•æ„Ÿè¯ã€‚";

SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
        .wordDeny(new MyWordDeny())
        .wordAllow(new MyWordAllow())
        .init();

Assert.assertEquals("[æˆ‘çš„è‡ªå®šä¹‰æ•æ„Ÿè¯]", wordBs.findAll(text).toString());

è¿™é‡Œåªæœ‰ æˆ‘çš„è‡ªå®šä¹‰æ•æ„Ÿè¯ æ˜¯æ•æ„Ÿè¯ï¼Œè€Œ 测试 䏿˜¯æ•感è¯ã€‚

当然,这里是全部使用我们自定义的实现,一般建议使用系统的默认é…ç½®+自定义é…置。

å¯ä»¥ä½¿ç”¨ä¸‹é¢çš„æ–¹å¼ã€‚

åŒæ—¶é…置多个

  • å¤šä¸ªæ•æ„Ÿè¯

WordDenys.chains() 方法,将多个实现åˆå¹¶ä¸ºåŒä¸€ä¸ª IWordDeny。

  • 多个白åå•

WordAllows.chains() 方法,将多个实现åˆå¹¶ä¸ºåŒä¸€ä¸ª IWordAllow。

例å­ï¼š

String text = "è¿™æ˜¯ä¸€ä¸ªæµ‹è¯•ã€‚æˆ‘çš„è‡ªå®šä¹‰æ•æ„Ÿè¯ã€‚";

IWordDeny wordDeny = WordDenys.chains(WordDenys.defaults(), new MyWordDeny());
IWordAllow wordAllow = WordAllows.chains(WordAllows.defaults(), new MyWordAllow());

SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
        .wordDeny(wordDeny)
        .wordAllow(wordAllow)
        .init();

Assert.assertEquals("[æˆ‘çš„è‡ªå®šä¹‰æ•æ„Ÿè¯]", wordBs.findAll(text).toString());

è¿™é‡Œéƒ½æ˜¯åŒæ—¶ä½¿ç”¨äº†ç³»ç»Ÿé»˜è®¤é…置,和自定义的é…置。

注æ„:我们åˆå§‹åŒ–了新的 wordBs,那么用新的 wordBs åŽ»åˆ¤æ–­ã€‚è€Œä¸æ˜¯ç”¨ä»¥å‰çš„ SensitiveWordHelper 工具方法,工具方法é…置是默认的ï¼

spring æ•´åˆ

背景

实际使用中,比如å¯ä»¥åœ¨é¡µé¢é…置修改,然åŽå®žæ—¶ç”Ÿæ•ˆã€‚

æ•°æ®å­˜å‚¨åœ¨æ•°æ®åº“ä¸­ï¼Œä¸‹é¢æ˜¯ä¸€ä¸ªä¼ªä»£ç çš„例å­ï¼Œå¯ä»¥å‚考 SpringSensitiveWordConfig.java

è¦æ±‚,版本 v0.0.15 åŠå…¶ä»¥ä¸Šã€‚

è‡ªå®šä¹‰æ•°æ®æº

简化伪代ç å¦‚下,数æ®çš„æºå¤´ä¸ºæ•°æ®åº“。

MyDdWordAllow å’Œ MyDdWordDeny 是基于数æ®åº“为æºå¤´çš„自定义实现类。

@Configuration
public class SpringSensitiveWordConfig {

    @Autowired
    private MyDdWordAllow myDdWordAllow;

    @Autowired
    private MyDdWordDeny myDdWordDeny;

    /**
     * åˆå§‹åŒ–引导类
     * @return åˆå§‹åŒ–引导类
     * @since 1.0.0
     */
    @Bean
    public SensitiveWordBs sensitiveWordBs() {
        SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance()
                .wordAllow(WordAllows.chains(WordAllows.defaults(), myDdWordAllow))
                .wordDeny(myDdWordDeny)
                // å„ç§å…¶ä»–é…ç½®
                .init();

        return sensitiveWordBs;
    }

}

æ•æ„Ÿè¯åº“çš„åˆå§‹åŒ–较为耗时,建议程åºå¯åŠ¨æ—¶åšä¸€æ¬¡ init åˆå§‹åŒ–。

Benchmark

V0.6.0 以åŽï¼Œæ·»åŠ å¯¹åº”çš„ benchmark 测试。

BenchmarkTimesTest

环境

测试环境为普通的笔记本:

处ç†å™¨	12th Gen Intel(R) Core(TM) i7-1260P   2.10 GHz
机带 RAM	16.0 GB (15.7 GB å¯ç”¨)
系统类型	64 使“作系统, 基于 x64 的处ç†å™¨

ps: ä¸åŒçŽ¯å¢ƒä¼šæœ‰å·®å¼‚ï¼Œä½†æ˜¯æ¯”ä¾‹åŸºæœ¬ç¨³å®šã€‚

测试效果记录

测试数æ®ï¼š100+ 字符串,循环 10W 次。

åºå· 场景 耗时 备注
1 åªå𿕿„Ÿè¯ï¼Œæ— ä»»ä½•æ ¼å¼è½¬æ¢ 1470ms,约 7.2W QPS 追求æžè‡´æ€§èƒ½ï¼Œå¯ä»¥è¿™æ ·é…ç½®
2 åªå𿕿„Ÿè¯ï¼Œæ”¯æŒå…¨éƒ¨æ ¼å¼è½¬æ¢ 2744ms,约 3.7W QPS 满足大部分场景

STAR

Stargazers over time

åŽæœŸ road-map

  • 移除å•ä¸ªæ±‰å­—çš„æ•æ„Ÿè¯ï¼Œåœ¨ä¸­å›½ï¼Œè¦æŠŠè¯ç»„当åšä¸€æ¬¡è¯ï¼Œé™ä½Žè¯¯åˆ¤çŽ‡ã€‚

  • 支æŒå•ä¸ªçš„æ•æ„Ÿè¯å˜åŒ–?

removeã€addã€edit?

  • æ•æ„Ÿè¯æ ‡ç­¾æŽ¥å£æ”¯æŒ

  • æ•æ„Ÿè¯å¤„ç†æ—¶æ ‡ç­¾æ”¯æŒ

  • wordData 的内存å ç”¨å¯¹æ¯” + 优化

  • 用户指定自定义的è¯ç»„ï¼ŒåŒæ—¶å…许指定è¯ç»„的组åˆèŽ·å–ï¼Œæ›´åŠ çµæ´»

FormatCombine/CheckCombine/AllowDenyCombine 组åˆç­–略,å…许用户自定义。

  • word check 策略的优化,统一é历+转æ¢

  • 添加 ThreadLocal 等性能优化

拓展阅读

æ•æ„Ÿè¯ç³»åˆ—

sensitive-word-admin æ•æ„Ÿè¯æŽ§å° v1.2.0 版本开æº

sensitive-word-admin v1.3.0 å‘布 如何支æŒåˆ†å¸ƒå¼éƒ¨ç½²ï¼Ÿ

01-å¼€æºæ•感è¯å·¥å…·å…¥é—¨ä½¿ç”¨

02-å¦‚ä½•å®žçŽ°ä¸€ä¸ªæ•æ„Ÿè¯å·¥å…·ï¼Ÿè¿ç¦è¯å®žçްæ€è·¯æ¢³ç†

03-æ•æ„Ÿè¯ä¹‹ StopWord åœæ­¢è¯ä¼˜åŒ–与特殊符å·

04-æ•æ„Ÿè¯ä¹‹å­—典瘦身

05-æ•æ„Ÿè¯ä¹‹ DFA 算法(Trie Tree 算法)详解

06-æ•æ„Ÿè¯(è„è¯) 如何忽略无æ„义的字符?达到更好的过滤效果

v0.10.0-è„è¯åˆ†ç±»æ ‡ç­¾åˆæ­¥æ”¯æŒ

v0.11.0-æ•æ„Ÿè¯æ–°ç‰¹æ€§ï¼šå¿½ç•¥æ— æ„ä¹‰çš„å­—ç¬¦ï¼Œè¯æ ‡ç­¾å­—å…¸

v0.12.0-æ•æ„Ÿè¯/è„è¯è¯æ ‡ç­¾èƒ½åŠ›è¿›ä¸€æ­¥å¢žå¼º

v0.13.0-æ•æ„Ÿè¯ç‰¹æ€§ç‰ˆæœ¬å‘布 支æŒè‹±æ–‡å•è¯å…¨è¯åŒ¹é…

v0.16.1-æ•æ„Ÿè¯æ–°ç‰¹æ€§ä¹‹å­—典内存资æºé‡Šæ”¾

v0.19.0-æ•æ„Ÿè¯æ–°ç‰¹æ€§ä¹‹æ•感è¯å•个编辑,ä¸å¿…é‡å¤åˆå§‹åŒ–

v0.20.0 æ•æ„Ÿè¯æ–°ç‰¹æ€§ä¹‹æ•°å­—全部匹é…ï¼Œè€Œä¸æ˜¯éƒ¨åˆ†åŒ¹é…

v0.21.0 æ•æ„Ÿè¯æ–°ç‰¹æ€§ä¹‹ç™½å啿”¯æŒå•个编辑,修正白åå•包å«é»‘å啿—¶çš„问题

v0.23.0 æ•æ„Ÿè¯ç»“æžœæ¡ä»¶æ‹“展,内置支æŒé“¾å¼+å•è¯æ ‡ç­¾

v0.24.0 æ–°ç‰¹æ€§æ”¯æŒæ ‡ç­¾åˆ†ç±»ï¼Œå†…置实现多ç§ç­–ç•¥

v0.25.0 新特性之 wordCheck 策略支æŒç”¨æˆ·è‡ªå®šä¹‰

v0.25.1 新特性之返回匹é…è¯ï¼Œä¿®æ­£ tags 标签

wechat

NLP å¼€æºçŸ©é˜µ

pinyin 汉字转拼音

pinyin2hanzi 拼音转汉字

segment 高性能中文分è¯

opencc4j 中文ç¹ç®€ä½“转æ¢

nlp-hanzi-similar 汉字相似度

word-checker 拼写检测

sensitive-word æ•æ„Ÿè¯

About

👮â€â™‚ï¸The sensitive word tool for java.(æ•æ„Ÿè¯/è¿ç¦è¯/è¿æ³•è¯/è„è¯ã€‚基于 DFA 算法实现的高性能 java æ•æ„Ÿè¯è¿‡æ»¤å·¥å…·æ¡†æž¶ã€‚内置支æŒå•è¯æ ‡ç­¾åˆ†ç±»åˆ†çº§ã€‚请勿å‘å¸ƒæ¶‰åŠæ”¿æ²»ã€å¹¿å‘Šã€è¥é”€ã€ç¿»å¢™ã€è¿åå›½å®¶æ³•å¾‹æ³•è§„ç­‰å†…å®¹ã€‚é«˜æ€§èƒ½æ•æ„Ÿè¯æ£€æµ‹è¿‡æ»¤ç»„件,附带ç¹ä½“简体互æ¢ï¼Œæ”¯æŒå…¨è§’åŠè§’互æ¢ï¼Œæ±‰å­—转拼音,模糊æœç´¢ç­‰åŠŸèƒ½ã€‚)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java 98.3%
  • Shell 1.2%
  • Batchfile 0.5%
0