feat: 添加敏感词模块 (#11)

This commit is contained in:
luoqiz
2025-02-14 21:21:05 +08:00
committed by GitHub
parent efb0eea050
commit 82f18356df
13 changed files with 420 additions and 0 deletions

View File

@@ -593,6 +593,13 @@
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 敏感词模块 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-sensitive-words</artifactId>
<version>${revision}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>top.continew</groupId>
<artifactId>continew-starter</artifactId>
<version>${revision}</version>
</parent>
<artifactId>continew-starter-sensitive-words</artifactId>
<description>Continew starter sensitive words 模块</description>
<dependencies>
<!-- Spring Boot Starter自动配置相关依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!-- Hutool工具类库 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-dfa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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.
*/
package top.continew.starter.sensitive.words.autoconfigure;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
/**
* JSR 303 校验器自动配置
*
* @author Charles7c
* @since 2.3.0
*/
@AutoConfiguration
public class SensitiveWordsAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(SensitiveWordsAutoConfiguration.class);
@PostConstruct
public void postConstruct() {
log.debug("[ContiNew Starter] - Auto Configuration 'sensitive words service' completed initialization.");
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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.
*/
package top.continew.starter.sensitive.words.autoconfigure;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Data
@Component
@ConfigurationProperties(prefix = "continew.sensitive-words")
public class SensitiveWordsProperties {
// 敏感词注入类型
private String type;
private List<String> values;
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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.
*/
package top.continew.starter.sensitive.words.service;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import top.continew.starter.sensitive.words.autoconfigure.SensitiveWordsProperties;
import java.util.List;
/**
* 默认敏感词配置
*/
@Component
@ConditionalOnProperty(prefix = "continew.sensitive-words", name = "type", havingValue = "default", matchIfMissing = true)
public class DefaultSensitiveWordsConfig implements SensitiveWordsConfig {
private final SensitiveWordsProperties properties;
public DefaultSensitiveWordsConfig(SensitiveWordsProperties properties) {
this.properties = properties;
}
@Override
public List<String> getWords() {
if (properties.getValues() != null) {
return properties.getValues();
}
return List.of();
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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.
*/
package top.continew.starter.sensitive.words.service;
import cn.hutool.dfa.WordTree;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 默认敏感词服务
*/
@Component
@ConditionalOnBean(SensitiveWordsConfig.class)
@ConditionalOnMissingBean(SensitiveWordsService.class)
public class DefaultSensitiveWordsService implements SensitiveWordsService {
private final SensitiveWordsConfig sensitiveWordsConfig;
private WordTree tree = new WordTree();
public DefaultSensitiveWordsService(SensitiveWordsConfig sensitiveWordsConfig) {
this.sensitiveWordsConfig = sensitiveWordsConfig;
if (sensitiveWordsConfig != null && sensitiveWordsConfig.getWords() != null) {
tree.addWords(sensitiveWordsConfig.getWords());
}
}
@Override
public List<String> check(String content) {
return tree.matchAll(content, -1, false, true);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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.
*/
package top.continew.starter.sensitive.words.service;
import java.util.List;
/**
* 敏感词配置
*/
public interface SensitiveWordsConfig {
List<String> getWords();
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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.
*/
package top.continew.starter.sensitive.words.service;
import java.util.List;
public interface SensitiveWordsService {
/**
* 检查敏感词
*
* @param content 待检测字符串
* @return 敏感词列表
*/
List<String> check(String content);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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.
*/
package top.continew.starter.sensitive.words.validate;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {SensitiveWordValidator.class})
public @interface SensitiveWord {
String message() default "有敏感词,请检测!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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.
*/
package top.continew.starter.sensitive.words.validate;
import jakarta.annotation.Resource;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import top.continew.starter.sensitive.words.service.SensitiveWordsService;
import java.util.List;
public class SensitiveWordValidator implements ConstraintValidator<SensitiveWord, String> {
@Resource
private SensitiveWordsService sensitiveWordsService;
/**
* 初始化方法,可以用自定义注解中获取值进行初始化
*
* @param {@link SensitiveWord } constraintAnnotation 注解值内容
*/
@Override
public void initialize(SensitiveWord constraintAnnotation) {
}
/**
* 实际校验自定义注解 value 值
*
* @param {@link String} value 待检测字符串
* @param {@link ConstraintValidatorContext } constraintValidatorContext 检测的上下文
* @return boolean 是否通过检测
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
List<String> res = sensitiveWordsService.check(value);
if (!res.isEmpty()) {
// 动态设置错误消息
context.disableDefaultConstraintViolation(); // 禁用默认消息
context.buildConstraintViolationWithTemplate("包含敏感词: " + String.join(",", res))
.addConstraintViolation();
return false;
}
return true;
}
}

View File

@@ -0,0 +1,4 @@
top.continew.starter.sensitive.words.autoconfigure.SensitiveWordsAutoConfiguration
top.continew.starter.sensitive.words.autoconfigure.SensitiveWordsProperties
top.continew.starter.sensitive.words.service.DefaultSensitiveWordsConfig
top.continew.starter.sensitive.words.service.DefaultSensitiveWordsService

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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.
*/
package top.continew.starter.sensitive.words;
import cn.hutool.dfa.FoundWord;
import cn.hutool.dfa.WordTree;
import java.util.List;
public class SensitiveWordsTest {
public static void main(String[] args) {
WordTree tree = new WordTree();
tree.addWord("");
tree.addWord("大土豆");
tree.addWord("土豆");
tree.addWord("刚出锅");
tree.addWord("出锅");
//正文
String text = "我有一颗大土豆,刚出锅的";
// 匹配到【大】,由于非密集匹配,因此从下一个字符开始查找,匹配到【土豆】接着被匹配
// 由于【刚出锅】被匹配,由于非密集匹配,【出锅】被跳过
List<String> matchAll = tree.matchAll(text, -1, false, true);
for (String s : matchAll) {
System.out.println(s);
}
System.out.println("-------------------");
String match = tree.match(text);
System.out.println(match);
System.out.println("-------------------");
FoundWord matchText = tree.matchWord(text);
System.out.println(matchText.getFoundWord());
}
}

View File

@@ -72,6 +72,7 @@
<module>continew-starter-auth</module> <module>continew-starter-auth</module>
<module>continew-starter-messaging</module> <module>continew-starter-messaging</module>
<module>continew-starter-extension</module> <module>continew-starter-extension</module>
<module>continew-starter-sensitive-words</module>
</modules> </modules>
<build> <build>