mirror of
https://github.com/houbb/sensitive-word.git
synced 2026-03-22 08:27:36 +08:00
release branch 0.0.15
This commit is contained in:
@@ -106,3 +106,9 @@
|
||||
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||
|:---|:---|:---|:---|:--|
|
||||
| 1 | A | 开发样式配置特性 | 2021-5-31 20:51:58 | |
|
||||
|
||||
# release_0.0.15
|
||||
|
||||
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||
|:---|:---|:---|:---|:--|
|
||||
| 1 | A | 优化 init 方式 | 2021-7-16 20:51:58 | |
|
||||
|
||||
86
README.md
86
README.md
@@ -38,6 +38,8 @@
|
||||
|
||||
- 支持用户自定义敏感词和白名单
|
||||
|
||||
- 支持数据的数据动态更新,实时生效
|
||||
|
||||
## 变更日志
|
||||
|
||||
[CHANGE_LOG.md](https://github.com/houbb/sensitive-word/blob/master/doc/CHANGE_LOG.md)
|
||||
@@ -56,7 +58,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>sensitive-word</artifactId>
|
||||
<version>0.0.14</version>
|
||||
<version>0.0.15</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -391,6 +393,88 @@ Assert.assertEquals("[我的自定义敏感词]", wordBs.findAll(text).toString(
|
||||
|
||||
这里都是同时使用了系统默认配置,和自定义的配置。
|
||||
|
||||
# spring 整合
|
||||
|
||||
## 背景
|
||||
|
||||
实际使用中,比如可以在页面配置修改,然后实时生效。
|
||||
|
||||
数据存储在数据库中,下面是一个伪代码的例子,可以参考 [SpringSensitiveWordConfig.java]()
|
||||
|
||||
要求,版本 v0.0.15 及其以上。
|
||||
|
||||
## 自定义数据源
|
||||
|
||||
简化伪代码如下,数据的源头为数据库。
|
||||
|
||||
MyDdWordAllow 和 MyDdWordDeny 是基于数据库为源头的自定义实现类。
|
||||
|
||||
```java
|
||||
@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.system(), myDdWordAllow))
|
||||
.wordDeny(myDdWordDeny)
|
||||
// 各种其他配置
|
||||
.init();
|
||||
|
||||
return sensitiveWordBs;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
敏感词库的初始化较为耗时,建议程序启动时做一次 init 初始化。
|
||||
|
||||
## 动态变更
|
||||
|
||||
为了保证敏感词修改可以实时生效且保证接口的尽可能简化,此处没有新增 add/remove 的方法。
|
||||
|
||||
而是在调用 `sensitiveWordBs.init()` 的时候,根据 IWordDeny+IWordAllow 重新构建敏感词库。
|
||||
|
||||
因为初始化可能耗时较长(秒级别),所有优化为 init 未完成时**不影响旧的词库功能,完成后以新的为准**。
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class SensitiveWordService {
|
||||
|
||||
@Autowired
|
||||
private SensitiveWordBs sensitiveWordBs;
|
||||
|
||||
/**
|
||||
* 更新词库
|
||||
*
|
||||
* 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法。
|
||||
* 如果需要生效,则调用这个方法。
|
||||
*
|
||||
* 说明:重新初始化不影响旧的方法使用。初始化完成后,会以新的为准。
|
||||
*/
|
||||
public void refresh() {
|
||||
// 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法,然后调用这个方法。
|
||||
sensitiveWordBs.init();
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
如上,你可以在数据库词库发生变更时,需要词库生效,主动触发一次初始化 `sensitiveWordBs.init();`。
|
||||
|
||||
其他使用保持不变,无需重启应用。
|
||||
|
||||
# 后期 road-map
|
||||
|
||||
- 停顿词
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>sensitive-word</artifactId>
|
||||
<version>0.0.14</version>
|
||||
<version>0.0.15</version>
|
||||
|
||||
<properties>
|
||||
<!--============================== All Plugins START ==============================-->
|
||||
|
||||
@@ -10,9 +10,9 @@ ECHO "============================= RELEASE START..."
|
||||
|
||||
:: 版本号信息(需要手动指定)
|
||||
:::: 旧版本名称
|
||||
SET version=0.0.14
|
||||
SET version=0.0.15
|
||||
:::: 新版本名称
|
||||
SET newVersion=0.0.15
|
||||
SET newVersion=0.0.16
|
||||
:::: 组织名称
|
||||
SET groupName=com.github.houbb
|
||||
:::: 项目名称
|
||||
|
||||
@@ -68,7 +68,10 @@ public class SensitiveWordBs {
|
||||
List<String> results = CollectionUtil.difference(denyList, allowList);
|
||||
|
||||
// 初始化 DFA 信息
|
||||
if(sensitiveWordMap == null) {
|
||||
sensitiveWordMap = new SensitiveWordMap();
|
||||
}
|
||||
// 便于可以多次初始化
|
||||
sensitiveWordMap.initWordMap(results);
|
||||
}
|
||||
|
||||
@@ -321,9 +324,14 @@ public class SensitiveWordBs {
|
||||
* @since 0.0.13
|
||||
*/
|
||||
private void statusCheck(){
|
||||
//DLC
|
||||
if(sensitiveWordMap == null) {
|
||||
synchronized (this) {
|
||||
if(sensitiveWordMap == null) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,15 +50,10 @@ public class SensitiveWordMap implements IWordMap {
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void initWordMap(Collection<String> collection) {
|
||||
// 避免重复加载
|
||||
if (MapUtil.isNotEmpty(innerWordMap)) {
|
||||
return;
|
||||
}
|
||||
|
||||
public synchronized void initWordMap(Collection<String> collection) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 避免扩容带来的消耗
|
||||
innerWordMap = new HashMap(collection.size());
|
||||
Map newInnerWordMap = new HashMap(collection.size());
|
||||
|
||||
for (String key : collection) {
|
||||
if (StringUtil.isEmpty(key)) {
|
||||
@@ -70,7 +65,7 @@ public class SensitiveWordMap implements IWordMap {
|
||||
final int size = chars.length;
|
||||
|
||||
// 每一个新词的循环,直接将结果设置为当前 map,所有变化都会体现在结果的 map 中
|
||||
Map currentMap = innerWordMap;
|
||||
Map currentMap = newInnerWordMap;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
// 截取敏感词当中的字,在敏感词库中字为HashMap对象的Key键值
|
||||
@@ -101,6 +96,9 @@ public class SensitiveWordMap implements IWordMap {
|
||||
}
|
||||
}
|
||||
|
||||
// 最后更新为新的 map,保证更新过程中旧的数据可用
|
||||
this.innerWordMap = newInnerWordMap;
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
System.out.println("Init sensitive word map end! Cost time: " + (endTime - startTime) + "ms");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.github.houbb.sensitive.word.spring;
|
||||
|
||||
import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
|
||||
import com.github.houbb.sensitive.word.spring.annotation.Autowired;
|
||||
import com.github.houbb.sensitive.word.spring.annotation.Bean;
|
||||
import com.github.houbb.sensitive.word.spring.annotation.Configuration;
|
||||
import com.github.houbb.sensitive.word.spring.database.MyDdWordAllow;
|
||||
import com.github.houbb.sensitive.word.spring.database.MyDdWordDeny;
|
||||
import com.github.houbb.sensitive.word.support.allow.WordAllows;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@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.system(), myDdWordAllow))
|
||||
.wordDeny(myDdWordDeny)
|
||||
// 各种其他配置
|
||||
.init();
|
||||
|
||||
return sensitiveWordBs;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.github.houbb.sensitive.word.spring.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Autowired {
|
||||
|
||||
boolean required() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.github.houbb.sensitive.word.spring.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Bean {
|
||||
|
||||
String[] value() default {};
|
||||
|
||||
String[] name() default {};
|
||||
|
||||
String initMethod() default "";
|
||||
|
||||
String destroyMethod() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.github.houbb.sensitive.word.spring.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Component {
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.github.houbb.sensitive.word.spring.annotation;
|
||||
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Component
|
||||
public @interface Configuration {
|
||||
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.github.houbb.sensitive.word.spring.database;
|
||||
|
||||
import com.github.houbb.sensitive.word.api.IWordAllow;
|
||||
import com.github.houbb.sensitive.word.spring.annotation.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
public class MyDdWordAllow implements IWordAllow {
|
||||
|
||||
@Override
|
||||
public List<String> allow() {
|
||||
// 数据库查询
|
||||
return Arrays.asList("学习");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.github.houbb.sensitive.word.spring.database;
|
||||
|
||||
import com.github.houbb.sensitive.word.api.IWordDeny;
|
||||
import com.github.houbb.sensitive.word.spring.annotation.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
public class MyDdWordDeny implements IWordDeny {
|
||||
|
||||
@Override
|
||||
public List<String> deny() {
|
||||
// 数据库返回的各种信息
|
||||
return Arrays.asList("广告");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.github.houbb.sensitive.word.spring.service;
|
||||
|
||||
import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
|
||||
import com.github.houbb.sensitive.word.spring.annotation.Autowired;
|
||||
import com.github.houbb.sensitive.word.spring.annotation.Component;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
public class SensitiveWordService {
|
||||
|
||||
@Autowired
|
||||
private SensitiveWordBs sensitiveWordBs;
|
||||
|
||||
/**
|
||||
* 更新词库
|
||||
*
|
||||
* 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法。
|
||||
* 如果需要生效,则调用这个方法。
|
||||
*
|
||||
* 说明:重新初始化不影响旧的方法使用。初始化完成后,会以新的为准。
|
||||
*/
|
||||
public void refresh() {
|
||||
// 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法,然后调用这个方法。
|
||||
sensitiveWordBs.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含
|
||||
*
|
||||
* 可以重新封装,也可以直接使用 sensitiveWordBs
|
||||
* @param word 单词
|
||||
* @return 结果
|
||||
*/
|
||||
public boolean contains(String word){
|
||||
return sensitiveWordBs.contains(word);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user