前言
在内容审核、用户评论、社区管理等场景中,敏感词检测是必不可少的功能。本文将介绍如何在 Spring Boot 项目中集成 sensitive-word 库,实现高效的敏感词检测。
tabs
- 用户昵称、签名过滤
- 评论、弹幕审核
- 论坛帖子过滤
- 聊天内容监管
- 客服质检/tab
- 🚀 高性能:基于 DFA 算法,14万+ QPS
- 🎯 准确率高:支持数字变体、重复词等检测
- 🔧 易集成:开箱即用,注解驱动
- 📦 功能丰富:内置大量敏感词库
- 🔌 可扩展:支持自定义敏感词和白名单/tab
- Spring Boot 2.x/3.x
- sensitive-word 0.18.0
- DFA 算法(确定有限状态自动机)
- 注解式编程/tab
项目地址:https://github.com/houbb/sensitive-word
一、快速开始
1.1 Maven 依赖
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>sensitive-word</artifactId>
<version>0.18.0</version>
</dependency>1.2 基础使用
import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
import com.github.houbb.sensitive.word.core.SensitiveWordHelper;
public class SensitiveWordDemo {
public static void main(String[] args) {
String text = "五星红旗迎风飘扬,毛主席的画像屹立在天安门前。";
// 1. 检测是否包含敏感词
boolean contains = SensitiveWordHelper.contains(text);
System.out.println("是否包含敏感词: " + contains); // true
// 2. 查找第一个敏感词
String first = SensitiveWordHelper.findFirst(text);
System.out.println("第一个敏感词: " + first); // 五星红旗
// 3. 查找所有敏感词
List<String> all = SensitiveWordHelper.findAll(text);
System.out.println("所有敏感词: " + all);
// [五星红旗, 毛主席, 天安门]
// 4. 替换敏感词
String replaced = SensitiveWordHelper.replace(text, '*');
System.out.println("替换后: " + replaced);
// ***迎风飘扬,***的画像屹立在***前。
}
}二、核心功能详解
2.1 四种检测方法
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
contains(String text) | 待检测文本 | boolean | 是否包含敏感词 |
findFirst(String text) | 待检测文本 | String | 第一个敏感词 |
findAll(String text) | 待检测文本 | List | 所有敏感词 |
replace(String text) | 待检测文本 | String | 替换敏感词为* |
String text = "这是一个测试,包含不良信息。";
// 快速检测
if (SensitiveWordHelper.contains(text)) {
// 包含敏感词,拒绝提交
throw new BusinessException("内容包含敏感词");
}
// 查找具体敏感词
String firstWord = SensitiveWordHelper.findFirst(text);
List<String> allWords = SensitiveWordHelper.findAll(text);
// 替换敏感词
String cleanText = SensitiveWordHelper.replace(text);2.2 数字变体检测
这个功能可以检测用户用数字替换字符的变体:
String text1 = "法轮功";
String text2 = "法0轮0功";
String text3 = "法O轮O功";
SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
.enableNumCheck(true) // 启用数字变体检测
.enableEnglishCheck(true) // 启用英文字母变体检测
.init();
System.out.println(wordBs.contains(text1)); // true
System.out.println(wordBs.contains(text2)); // true
System.out.println(wordBs.contains(text3)); // true
注意:数字变体检测会增加计算量,根据实际需求选择是否开启。
三、Spring Boot 集成
3.1 配置文件
在 application.yaml 中配置敏感词检测:
sensitive-word:
# 是否启用敏感词检测,默认为 true
enabled: true
# 敏感词检测模式
# true=严格模式(检测所有),false=快速模式(发现第一个就返回)
strict-mode: false
# 是否启用数字变体检测
# 例如:将 "法轮功" 替换为 "法0轮0功"
enable-num-check: true
# 是否启用邮箱检测
enable-email-check: true
# 是否启用 URL 检测
enable-url-check: true
# 是否启用 IPv4 检测
enable-ipv4-check: false
# 自定义敏感词列表
custom-words:
- 自定义敏感词1
- 自定义敏感词2
# 自定义白名单列表
white-list:
- 允许的词1
- 允许的词23.2 注解使用
字段级别检测
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import com.yourpackage.framework.sensitiveword.core.annotation.SensitiveWordCheck;
@Data
@Schema(description = "用户信息修改请求参数")
public class UserProfileEditReqVO {
@Schema(description = "用户ID")
private Long userId;
@Schema(description = "用户昵称")
@SensitiveWordCheck(value = "用户昵称")
private String nickname;
@Schema(description = "个人简介")
@SensitiveWordCheck(value = "个人简介")
private String bio;
}方法级别检测
import com.yourpackage.framework.sensitiveword.core.annotation.SensitiveWordValidate;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user/profile")
public class UserProfileController {
@Resource
private UserService userService;
@PostMapping("/update")
public CommonResult<Boolean> updateUserProfile(
@RequestBody
@Valid
@SensitiveWordValidate // 触发敏感词检测
UserProfileEditReqVO editReqVO
) {
return CommonResult.success(userService.updateProfile(editReqVO));
}
}Service 层检测
import com.yourpackage.framework.sensitiveword.core.annotation.SensitiveWordValidate;
@Service
public class UserServiceImpl implements UserService {
@Override
public Boolean updateProfile(@SensitiveWordValidate UserProfileEditReqVO editReqVO) {
// 方法参数会被自动检测
// 如果包含敏感词,会抛出 SensitiveWordException
// ...
}
}3.3 完整工作流程
客户端请求
↓
Controller 层
↓
@SensitiveWordValidate 注解检测
↓
包含敏感词?
├─ 是 → 抛出 SensitiveWordException → 返回 400 错误
└─ 否 → Service 层处理 → 持久化 → 返回成功实际案例:
// 1. 用户提交昵称修改请求
POST /user/profile/update
Content-Type: application/json
{
"nickname": "我是主席", // 包含敏感词
"avatarUrl": "https://example.com/avatar.jpg"
}
// 2. 后端检测流程
@SensitiveWordValidate UserProfileEditReqVO editReqVO
↓
检测到 "主席" 是敏感词
↓
抛出 SensitiveWordException: 用户昵称包含敏感词: 主席
↓
返回 400 错误
// 3. 响应
{
"code": 400,
"msg": "用户昵称包含敏感词: 主席",
"data": null
}四、自定义敏感词和白名单
4.1 配置文件方式
在 application.yaml 中配置:
sensitive-word:
# 自定义敏感词
custom-words:
- 垃圾信息
- 广告词
- 违法词汇
# 白名单(这些词不会被检测为敏感词)
white-list:
- 主席
- 总统
- 领导4.2 编程方式
import com.github.houbb.sensitive.word.support.allow.WordAllows;
import com.github.houbb.sensitive.word.support.deny.WordDenys;
import java.util.Arrays;
import java.util.List;
// 自定义敏感词列表
List<String> myDenyWords = Arrays.asList(
"垃圾信息",
"广告词",
"违法词汇"
);
// 自定义白名单列表
List<String> myAllowWords = Arrays.asList(
"主席",
"总统"
);
SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
.wordDeny(WordDenys.chain()
.add(myDenyWords) // 添加自定义敏感词
.add(WordDenys.defaults()) // 保留默认敏感词库
)
.wordAllow(WordAllows.chain()
.add(myAllowWords) // 添加自定义白名单
.add(WordAllows.defaults()) // 保留默认白名单
)
.init();4.3 动态加载(数据库)
import com.github.houbb.sensitive.word.support.deny.IWordDeny;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class DatabaseWordDeny implements IWordDeny {
@Resource
private SensitiveWordMapper sensitiveWordMapper;
@Override
public List<String> deny() {
// 从数据库加载敏感词列表
return sensitiveWordMapper.selectAll();
}
}使用方式:
SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
.wordDeny(new DatabaseWordDeny())
.init();五、高级配置
5.1 自定义替换字符
String replaced = SensitiveWordHelper.replace(text, "❌");
// 输出:这是一个❌,包含❌。5.2 自定义替换策略
import com.github.houbb.sensitive.word.support.replace.WordReplace;
// 自定义替换策略
WordReplace replace = (word, stringBuilder) -> {
stringBuilder.append("[屏蔽]");
};
String result = SensitiveWordBs.newInstance()
.wordReplace(replace)
.init()
.replace(text);5.3 指定检测类型
import com.github.houbb.sensitive.word.bs.SensitiveWordBs;
SensitiveWordBs wordBs = SensitiveWordBs.newInstance()
// 只检测数字
.wordDeny(WordDenys.defaults())
// 白名单
.wordAllow(WordAllows.defaults())
// 启用邮箱检测
.enableEmailCheck(true)
// 启用 URL 检测
.enableUrlCheck(true)
// 启用数字变体检测
.enableNumCheck(true)
.init();六、性能优化
6.1 性能指标
根据官方测试数据(机器配置:i7-6700, 16GB RAM):
| 场景 | QPS |
|---|---|
| 简单文本检测 | 140,000+ |
| 复杂文本检测 | 100,000+ |
| 数字变体检测 | 80,000+ |
| 大量敏感词库 | 50,000+ |
提示:实际性能取决于机器配置、词典大小和检测策略。
6.2 优化建议
tabs
sensitive-word:
strict-mode: false # 快速模式- 检测到第一个敏感词就返回
- 适合实时验证场景
- 性能提升 30-50%/tab
sensitive-word:
enable-email-check: false
enable-url-check: false
enable-ipv4-check: false
enable-num-check: false- 只启用必需的检测
- 减少不必要的计算
- 性能提升 20-40%/tab
@Component
public class SensitiveWordCache {
private final Cache<String, Boolean> cache =
Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(1, TimeUnit.HOURS)
.build();
public boolean contains(String text) {
return cache.get(text, key ->
SensitiveWordHelper.contains(key)
);
}
}- 重复内容直接返回缓存结果
- 适合热点内容检测
- 性能提升 90%+/tab
@Async
public void checkAsync(String content, Consumer<Boolean> callback) {
boolean result = SensitiveWordHelper.contains(content);
callback.accept(result);
}- 非阻塞式检测
- 适合高并发场景
- 提升系统吞吐量/tab
6.3 批量检测优化
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public class BatchSensitiveWordChecker {
private final ExecutorService executor =
Executors.newFixedThreadPool(10);
/**
* 批量检测敏感词
*/
public List<String> batchCheck(List<String> texts) {
List<Future<String>> futures = texts.stream()
.map(text -> executor.submit(() -> {
if (SensitiveWordHelper.contains(text)) {
return text;
}
return null;
}))
.collect(Collectors.toList());
return futures.stream()
.map(future -> {
try {
return future.get();
} catch (Exception e) {
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}七、异常处理
7.1 自定义异常处理
import com.yourpackage.framework.sensitiveword.core.exception.SensitiveWordException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class SensitiveWordExceptionHandler {
@ExceptionHandler(SensitiveWordException.class)
public CommonResult<?> handleSensitiveWordException(SensitiveWordException e) {
// 获取检测到的敏感词列表
List<String> words = e.getWords();
// 记录日志
log.warn("检测到敏感词: {}", words);
// 返回友好的错误信息
return CommonResult.error(400,
"内容包含敏感词,请修改后提交");
}
}7.2 敏感词记录
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class SensitiveWordLogAspect {
@Before("@annotation(check)")
public void logSensitiveWord(SensitiveWordCheck check) {
log.info("检测字段: {}", check.value());
}
@Before("@annotation(validate)")
public void logSensitiveWordValidate(SensitiveWordValidate validate) {
log.info("执行敏感词检测");
}
}八、实战案例
8.1 用户昵称过滤
需求:用户修改昵称时,检测并拒绝包含敏感词的昵称。
实现:
@Data
@Schema(description = "用户信息修改请求参数")
public class UserProfileEditReqVO {
@Schema(description = "用户昵称")
@SensitiveWordCheck(value = "用户昵称")
private String nickname;
}测试:
// 测试用例1:正常昵称
{
"nickname": "快乐的小猪"
}
// 结果:通过 ✅
// 测试用例2:包含敏感词
{
"nickname": "我是主席"
}
// 结果:拒绝 ❌
// 返回:用户昵称包含敏感词: 主席8.2 评论内容审核
需求:用户提交评论时,自动替换敏感词。
实现:
@Service
public class CommentService {
public void createComment(CommentCreateReqVO reqVO) {
String content = reqVO.getContent();
// 替换敏感词
String cleanContent = SensitiveWordHelper.replace(content);
// 保存清理后的内容
CommentDO comment = new CommentDO();
comment.setContent(cleanContent);
commentMapper.insert(comment);
// 如果内容被修改,通知用户
if (!content.equals(cleanContent)) {
log.info("评论内容包含敏感词,已自动替换");
}
}
}8.3 批量内容审核
需求:管理员批量审核用户提交的内容。
实现:
@Service
public class ContentAuditService {
/**
* 批量审核内容
*/
public AuditResult batchAudit(List<String> contents) {
AuditResult result = new AuditResult();
for (String content : contents) {
// 检测敏感词
List<String> words = SensitiveWordHelper.findAll(content);
if (!words.isEmpty()) {
// 记录违规内容
result.addViolation(content, words);
}
}
return result;
}
/**
* 获取内容审核报告
*/
public String generateReport(List<String> contents) {
StringBuilder report = new StringBuilder();
report.append("内容审核报告\n");
report.append("==========================================\n");
int total = contents.size();
int passed = 0;
int rejected = 0;
for (String content : contents) {
List<String> words = SensitiveWordHelper.findAll(content);
if (words.isEmpty()) {
passed++;
report.append("✅ 通过: ").append(content).append("\n");
} else {
rejected++;
report.append("❌ 拒绝: ").append(content).append("\n");
report.append(" 敏感词: ").append(words).append("\n");
}
}
report.append("==========================================\n");
report.append("总计: ").append(total).append("\n");
report.append("通过: ").append(passed).append("\n");
report.append("拒绝: ").append(rejected).append("\n");
return report.toString();
}
}九、最佳实践
9.1 敏感词管理
tabs
- 从官方仓库更新敏感词库
- 根据业务需求添加自定义词
- 定期审查白名单/tab
- 普通敏感词:警告并替换
- 严重敏感词:直接拒绝
- 白名单词汇:允许通过/tab
- 敏感词列表纳入版本管理
- 记录每次修改的原因
- 支持快速回滚/tab
- 记录所有敏感词检测
- 统计敏感词出现频率
- 分析违规趋势/tab
9.2 性能监控
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Slf4j
@Aspect
@Component
public class SensitiveWordPerformanceAspect {
@Around("@annotation(validate)")
public Object monitorPerformance(ProceedingJoinPoint pjp,
SensitiveWordValidate validate)
throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
long duration = System.currentTimeMillis() - start;
// 记录检测耗时
if (duration > 100) {
log.warn("敏感词检测耗时: {}ms", duration);
} else {
log.info("敏感词检测耗时: {}ms", duration);
}
}
}
}9.3 降级策略
@Service
public class SensitiveWordService {
@Value("${sensitive-word.enabled:true}")
private boolean enabled;
public boolean check(String text) {
if (!enabled) {
// 降级:敏感词检测关闭时直接通过
return false;
}
try {
return SensitiveWordHelper.contains(text);
} catch (Exception e) {
// 降级:检测失败时记录日志但放行
log.error("敏感词检测异常,降级通过", e);
return false;
}
}
}十、总结
核心要点:sensitive-word 是一款高性能的敏感词检测库,基于 DFA 算法,支持多种检测策略和自定义扩展。
主要优势:
- 高性能:基于 DFA 算法,单机 14万+ QPS
- 易集成:注解驱动,开箱即用
- 功能丰富:支持数字变体、邮箱、URL 等检测
- 可扩展:支持自定义敏感词和白名单
- 线程安全:初始化后可安全用于多线程环境
适用场景:
- ✅ 用户昵称、签名过滤
- ✅ 评论、弹幕审核
- ✅ 论坛帖子过滤
- ✅ 聊天内容监管
- ✅ 客服质检
参考资料: