diff --git a/continew-starter-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index 238e538c..e53694dc 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -593,6 +593,13 @@ ${revision} + + + top.continew + continew-starter-sensitive-words + ${revision} + + diff --git a/continew-starter-sensitive-words/pom.xml b/continew-starter-sensitive-words/pom.xml new file mode 100644 index 00000000..1a785c09 --- /dev/null +++ b/continew-starter-sensitive-words/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + top.continew + continew-starter + ${revision} + + + continew-starter-sensitive-words + Continew starter sensitive words 模块 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-configuration-processor + + + + + cn.hutool + hutool-dfa + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.projectlombok + lombok + + + \ No newline at end of file diff --git a/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/autoconfigure/SensitiveWordsAutoConfiguration.java b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/autoconfigure/SensitiveWordsAutoConfiguration.java new file mode 100644 index 00000000..0ca5a3b7 --- /dev/null +++ b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/autoconfigure/SensitiveWordsAutoConfiguration.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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."); + } +} diff --git a/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/autoconfigure/SensitiveWordsProperties.java b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/autoconfigure/SensitiveWordsProperties.java new file mode 100644 index 00000000..f8ae551d --- /dev/null +++ b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/autoconfigure/SensitiveWordsProperties.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 values; +} diff --git a/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/DefaultSensitiveWordsConfig.java b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/DefaultSensitiveWordsConfig.java new file mode 100644 index 00000000..d5fba31f --- /dev/null +++ b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/DefaultSensitiveWordsConfig.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 getWords() { + if (properties.getValues() != null) { + return properties.getValues(); + } + return List.of(); + } +} diff --git a/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/DefaultSensitiveWordsService.java b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/DefaultSensitiveWordsService.java new file mode 100644 index 00000000..9716651e --- /dev/null +++ b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/DefaultSensitiveWordsService.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 check(String content) { + return tree.matchAll(content, -1, false, true); + } +} diff --git a/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/SensitiveWordsConfig.java b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/SensitiveWordsConfig.java new file mode 100644 index 00000000..e63487d7 --- /dev/null +++ b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/SensitiveWordsConfig.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 getWords(); + +} diff --git a/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/SensitiveWordsService.java b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/SensitiveWordsService.java new file mode 100644 index 00000000..fe0794e2 --- /dev/null +++ b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/service/SensitiveWordsService.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 check(String content); +} diff --git a/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/validate/SensitiveWord.java b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/validate/SensitiveWord.java new file mode 100644 index 00000000..6efb1369 --- /dev/null +++ b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/validate/SensitiveWord.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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[] payload() default {}; +} \ No newline at end of file diff --git a/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/validate/SensitiveWordValidator.java b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/validate/SensitiveWordValidator.java new file mode 100644 index 00000000..27239ace --- /dev/null +++ b/continew-starter-sensitive-words/src/main/java/top/continew/starter/sensitive/words/validate/SensitiveWordValidator.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 { + + @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 res = sensitiveWordsService.check(value); + if (!res.isEmpty()) { + // 动态设置错误消息 + context.disableDefaultConstraintViolation(); // 禁用默认消息 + context.buildConstraintViolationWithTemplate("包含敏感词: " + String.join(",", res)) + .addConstraintViolation(); + return false; + } + return true; + } +} \ No newline at end of file diff --git a/continew-starter-sensitive-words/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/continew-starter-sensitive-words/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..72906200 --- /dev/null +++ b/continew-starter-sensitive-words/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -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 \ No newline at end of file diff --git a/continew-starter-sensitive-words/src/test/java/top/continew/starter/sensitive/words/SensitiveWordsTest.java b/continew-starter-sensitive-words/src/test/java/top/continew/starter/sensitive/words/SensitiveWordsTest.java new file mode 100644 index 00000000..08a13561 --- /dev/null +++ b/continew-starter-sensitive-words/src/test/java/top/continew/starter/sensitive/words/SensitiveWordsTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 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()); + } +} diff --git a/pom.xml b/pom.xml index cd3a2843..9076a968 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,7 @@ continew-starter-auth continew-starter-messaging continew-starter-extension + continew-starter-sensitive-words