SpringBoot实现过滤敏感词的示例代码

 更新时间:2022年08月05日 10:55:19   作者:嘿_鱼骨头  
这篇文章主要为大家详细介绍了如何利用SpringBoot实现过滤敏感词功能,文中的示例代码讲解详细,感兴趣的小伙伴可以动手尝试一下

过滤敏感词

1. 创建一个储存要过滤的敏感词的文本文件

首先创建一个文本文件储存要过滤的敏感词

在下面的工具类中我们会读取这个文本文件,这里提前给出

@PostConstruct   // 这个注解表示当容器实例化这个bean(服务启动的时候)之后在调用构造器之后这个方法会自动的调用
public void init(){
    try(
            // 读取写有“敏感词”的文件,getClass表示从程序编译之后的target/classes读配置文件,读之后是字节流
            // java7语法,在这里的句子最后会自动执行close语句
            InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
            // 字节流  ->   字符流  ->  缓冲流
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));

    ) {
        String keyword;
        // 从文件中一行一行读
        while ((keyword = reader.readLine()) != null){
            // 添加到前缀树
            this.addKeyword(keyword);
        }
    } catch (IOException e) {
        logger.error("加载敏感词文件失败: " + e.getMessage());
    }
}

2. 开发过滤敏感词的工具类

开发过滤敏感词组件

为了方便以后复用,我们把过滤敏感词写成一个工具类SensitiveFilter。

@Component
public class SensitiveFilter {

    private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);

    // 当检测到敏感词后我们要把敏感词替换成什么符号
    private static final String REPLACEMENT = "***";

    // 根节点
    private TrieNode rootNode = new TrieNode();

    @PostConstruct   // 这个注解表示当容器实例化这个bean(服务启动的时候)之后在调用构造器之后这个方法会自动的调用
    public void init(){
        try(
                // 读取写有“敏感词”的文件,getClass表示从程序编译之后的target/classes读配置文件,读之后是字节流
                // java7语法,在这里的句子最后会自动执行close语句
                InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
                // 字节流  ->   字符流  ->  缓冲流
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));

        ) {
            String keyword;
            // 从文件中一行一行读
            while ((keyword = reader.readLine()) != null){
                // 添加到前缀树
                this.addKeyword(keyword);
            }
        } catch (IOException e) {
            logger.error("加载敏感词文件失败: " + e.getMessage());
        }
    }

    // 将一个敏感词添加到前缀树中
    private void addKeyword(String keyword){
        // 首先默认指向根
        TrieNode tempNode = rootNode;
        for (int i = 0; i < keyword.length(); i++) {
            char c = keyword.charAt(i);
            TrieNode subNode = tempNode.getSubNode(c);
            if(subNode == null){
                // subNode为空,初始化子节点;subNode不为空,直接用就可以了
                subNode = new TrieNode();
                tempNode.addSubNode(c, subNode);
            }
            // 指针指向子节点,进入下一轮循环
            tempNode = subNode;
        }
        // 最后要设置结束标识
        tempNode.setKeywordEnd(true);
    }

    /**
     * 过滤敏感词
     * @param text 待过滤的文本
     * @return  过滤后的文本
     */
    public String filter(String text){
        if(StringUtils.isBlank(text)){
            // 待过滤的文本为空,直接返回null
            return null;
        }
        // 指针1,指向树
        TrieNode tempNode = rootNode;
        // 指针2,指向正在检测的字符串段的首
        int begin = 0;
        // 指针3,指向正在检测的字符串段的尾
        int position = 0;
        // 储存过滤后的文本
        StringBuilder sb = new StringBuilder();
        while (begin < text.length()){
            char c = text.charAt(position);

            // 跳过符号,比如 “开票”是敏感词 #开#票# 这个字符串中间的 '#' 应该跳过
            if(isSymbol(c)){
                // 是特殊字符
                // 若指针1处于根节点,将此符号计入结果,指针2、3向右走一步
                if(tempNode == rootNode){
                    sb.append(c);
                    begin++;
                }
                // 无论符号在开头或中间,指针3都向下走一步
                position++;
                // 符号处理完,进入下一轮循环
                continue;
            }
            // 执行到这里说明字符不是特殊符号
            // 检查下级节点
            tempNode = tempNode.getSubNode(c);
            if(tempNode == null){
                // 以begin开头的字符串不是敏感词
                sb.append(text.charAt(begin));
                // 进入下一个位置
                position = ++begin;
                // 重新指向根节点
                tempNode = rootNode;
            } else if(tempNode.isKeywordEnd()){
                // 发现敏感词,将begin~position字符串替换掉,存 REPLACEMENT (里面是***)
                sb.append(REPLACEMENT);
                // 进入下一个位置
                begin = ++position;
                // 重新指向根节点
                tempNode = rootNode;
            } else {
                // 检查下一个字符
                position++;
            }
        }
        return sb.toString();
    }

    // 判断是否为特殊符号,是则返回true,不是则返回false
    private boolean isSymbol(Character c){
        // CharUtils.isAsciiAlphanumeric(c)方法:a、b、1、2···返回true,特殊字符返回false
        // 0x2E80  ~  0x9FFF 是东亚的文字范围,东亚文字范围我们不认为是符号
        return  !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
    }

    // 前缀树
    private class TrieNode{

        // 关键词结束标识
        private boolean isKeywordEnd = false;

        // 当前节点的子节点(key是下级字符、value是下级节点)
        private Map<Character, TrieNode> subNodes = new HashMap<>();

        public boolean isKeywordEnd() {
            return isKeywordEnd;
        }

        public void setKeywordEnd(boolean keywordEnd) {
            isKeywordEnd = keywordEnd;
        }

        // 添加子节点
        public void addSubNode(Character c, TrieNode node){
            subNodes.put(c, node);
        }

        // 获取子节点
        public TrieNode getSubNode(Character c){
            return subNodes.get(c);
        }
    }
}

上面就是过滤敏感词工具类的全部代码,接下来我们来解释一下开发步骤

开发过滤敏感词组件分为三步:

1.定义前缀树(Tree)

我们将定义前缀树写为SensitiveFilter工具类的内部类

// 前缀树
private class TrieNode{

    // 关键词结束标识
    private boolean isKeywordEnd = false;

    // 当前节点的子节点(key是下级字符、value是下级节点)
    private Map<Character, TrieNode> subNodes = new HashMap<>();

    public boolean isKeywordEnd() {
        return isKeywordEnd;
    }

    public void setKeywordEnd(boolean keywordEnd) {
        isKeywordEnd = keywordEnd;
    }

    // 添加子节点
    public void addSubNode(Character c, TrieNode node){
        subNodes.put(c, node);
    }

    // 获取子节点
    public TrieNode getSubNode(Character c){
        return subNodes.get(c);
    }
}

2.根据敏感词,初始化前缀树

将敏感词添加到前缀树中

// 将一个敏感词添加到前缀树中
private void addKeyword(String keyword){
    // 首先默认指向根
    TrieNode tempNode = rootNode;
    for (int i = 0; i < keyword.length(); i++) {
        char c = keyword.charAt(i);
        TrieNode subNode = tempNode.getSubNode(c);
        if(subNode == null){
            // subNode为空,初始化子节点;subNode不为空,直接用就可以了
            subNode = new TrieNode();
            tempNode.addSubNode(c, subNode);
        }
        // 指针指向子节点,进入下一轮循环
        tempNode = subNode;
    }
    // 最后要设置结束标识
    tempNode.setKeywordEnd(true);
}

3.编写过滤敏感词的方法

如何过滤文本中的敏感词:

特殊符号怎么处理:

敏感词前缀树初始化完毕之后,过滤文本中的敏感词的算法应该如下:

定义三个指针:

  • 指针1指向Tree树
  • 指针2指向待过滤字符串段
  • 指针3指向待过滤字符串段
/**
 * 过滤敏感词
 * @param text 待过滤的文本
 * @return  过滤后的文本
 */
public String filter(String text){
    if(StringUtils.isBlank(text)){
        // 待过滤的文本为空,直接返回null
        return null;
    }
    // 指针1,指向树
    TrieNode tempNode = rootNode;
    // 指针2,指向正在检测的字符串段的首
    int begin = 0;
    // 指针3,指向正在检测的字符串段的尾
    int position = 0;
    // 储存过滤后的文本
    StringBuilder sb = new StringBuilder();
    while (begin < text.length()){
        char c = text.charAt(position);

        // 跳过符号,比如 “开票”是敏感词 #开#票# 这个字符串中间的 '#' 应该跳过
        if(isSymbol(c)){
            // 是特殊字符
            // 若指针1处于根节点,将此符号计入结果,指针2、3向右走一步
            if(tempNode == rootNode){
                sb.append(c);
                begin++;
            }
            // 无论符号在开头或中间,指针3都向下走一步
            position++;
            // 符号处理完,进入下一轮循环
            continue;
        }
        // 执行到这里说明字符不是特殊符号
        // 检查下级节点
        tempNode = tempNode.getSubNode(c);
        if(tempNode == null){
            // 以begin开头的字符串不是敏感词
            sb.append(text.charAt(begin));
            // 进入下一个位置
            position = ++begin;
            // 重新指向根节点
            tempNode = rootNode;
        } else if(tempNode.isKeywordEnd()){
            // 发现敏感词,将begin~position字符串替换掉,存 REPLACEMENT (里面是***)
            sb.append(REPLACEMENT);
            // 进入下一个位置
            begin = ++position;
            // 重新指向根节点
            tempNode = rootNode;
        } else {
            // 检查下一个字符
            position++;
        }
    }
    return sb.toString();
}

// 判断是否为特殊符号,是则返回true,不是则返回false
private boolean isSymbol(Character c){
    // CharUtils.isAsciiAlphanumeric(c)方法:a、b、1、2···返回true,特殊字符返回false
    // 0x2E80  ~  0x9FFF 是东亚的文字范围,东亚文字范围我们不认为是符号
    return  !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
}

最后:建议在测试类中测试一下

经测试,过滤敏感词的工具类开发完成,这个工具会在接下来的发布帖子的功能中用到。

以上就是SpringBoot实现过滤敏感词的示例代码的详细内容,更多关于SpringBoot过滤敏感词的资料请关注脚本之家其它相关文章!

相关文章

  • 使用Java连接WMI获取系统信息的方法

    使用Java连接WMI获取系统信息的方法

    在 Windows 系统上,WMI是一套管理数据和操作系统组件的基础设施,能让应用程序查询系统信息、监控硬件状态以及管理系统配置,本项目旨在讲解如何通过 Java 连接 WMI 获取系统信息,需要的朋友可以参考下
    2025-03-03
  • java实现静默加载Class示例代码

    java实现静默加载Class示例代码

    这篇文章主要给大家介绍了关于java实现静默加载Class的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用java具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-10-10
  • java实现递归菜单树

    java实现递归菜单树

    这篇文章主要为大家详细介绍了java实现递归菜单树,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • SpringBoot数据压缩实践方案

    SpringBoot数据压缩实践方案

    文章探讨了Redis中处理大key的压缩方案,包括拆分JSON结构、使用GZIP/Deflater压缩、优化体积(如移除空格、缩短键名)、流式处理及分段存储,旨在降低内存负担并提升性能,感兴趣的朋友跟随小编一起看看吧
    2025-10-10
  • SpringBoot整合spring-retry实现接口请求重试机制及注意事项

    SpringBoot整合spring-retry实现接口请求重试机制及注意事项

    今天通过本文给大家介绍我们应该如何使用SpringBoot来整合spring-retry组件实现重试机制及注意事项,本文通过实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2021-08-08
  • Java中Stream流的使用示例详解

    Java中Stream流的使用示例详解

    Stream流提供了一种高效且易于使用的方式来对数据进行过滤、映射、排序、聚合等操作,使代码更加简洁和可读,接下来通过本文给大家介绍Java中Stream流的使用,感兴趣的朋友跟随小编一起看看吧
    2025-08-08
  • 详解Java中Quartz的简单使用

    详解Java中Quartz的简单使用

    Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中。这篇文章主要通过示例和大家讲讲Quartz的简单使用,需要的可以参考一下
    2023-04-04
  • Java中super与this关键字的用途及区别详解

    Java中super与this关键字的用途及区别详解

    这篇文章主要介绍了Java中super与this关键字的用途及区别的相关资料,super和this是Java中用于引用父类和当前对象的特殊关键字,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-04-04
  • SpringBoot实现全局异常处理方法总结

    SpringBoot实现全局异常处理方法总结

    在项目开发中出现异常时很平常不过的事情,我们处理异常也有很多种方式。本文将详细为大家讲解SpringBoot实现全局异常处理几种方法,感兴趣的可以学习一下
    2022-03-03
  • 高内聚低耦合原则_动力节点Java学院整理

    高内聚低耦合原则_动力节点Java学院整理

    耦合度就是某模块(类)与其它模块(类)之间的关联、感知和依赖的程度,是衡量代码独立性的一个指标,也是软件工程设计及编码质量评价的一个标准
    2017-08-08

最新评论