zoukankan      html  css  js  c++  java
  • java 如何实现开箱即用的敏感词控台服务?

    sensitive-word-admin

    sensitive-word-admin 是基于 sensitive-word 实现的,
    一款开箱即用的敏感词控台服务。

    特性

    • 基本的 CRUD

    • 开箱即用的配置控台

    • 简单易用的 API 服务

    变更日志

    快速开始

    数据库脚本

    执行 mysql-5.7.sql 脚本。

    核心表如下:

    create table word
    (
        id int unsigned auto_increment comment '应用自增主键' primary key,
        word varchar(128) not null comment '单词',
        type varchar(8) not null comment '类型',
        status char(1) not null default 'S' comment '状态',
        remark varchar(64) not null comment '配置描述' default '',
        operator_id varchar(64) not null default 'system' comment '操作员名称',
        create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间戳',
        update_time timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间戳'
    ) comment '敏感词表' ENGINE=Innodb default charset=UTF8 auto_increment=1;
    create unique index uk_word on word (word) comment '唯一索引';
    

    应用启动

    直接运行 Applicaiton#main() 启动应用,启动日志如下:

    2021-07-20 20:56:48.200  INFO [] 6680 --- [           main] o.a.coyote.http11.Http11NioProtocol      : Starting ProtocolHandler ["http-nio-8080"]
    2021-07-20 20:56:48.219  INFO [] 6680 --- [           main] o.a.tomcat.util.net.NioSelectorPool      : Using a shared selector for servlet write/read
    2021-07-20 20:56:48.248  INFO [] 6680 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
    2021-07-20 20:56:48.256  INFO [] 6680 --- [           main] c.g.h.sensitive.word.admin.Application   : Started Application in 14.04 seconds (JVM running for 15.82)
    

    敏感词配置

    除了 sensitive-word 本身自带的敏感词外,我们可以根据自己的业务进行配置。

    config.png

    类型分为两种:禁止和允许。禁止说明是敏感词,允许说明不认为是敏感词。

    所有的类型,只要在状态为正常的情况下才会生效。

    测试验证

    contains

    是否包含敏感词。

    http://localhost:8080/api/sensitiveWord/contains?text=凡凡做测试
    

    返回:

    {"respCode":"0000","respMessage":"成功","result":true}
    

    findAll

    找到所有的敏感词。

    http://localhost:8080/api/sensitiveWord/findAll?text=凡凡做测试
    

    返回:

    {"respCode":"0000","respMessage":"成功","total":2,"list":["凡凡","测试"]}
    

    replace

    替换对应的敏感词。

    http://localhost:8080/api/sensitiveWord/replace?text=凡凡做测试
    

    返回:

    {"respCode":"0000","respMessage":"成功","result":"**做**"}
    

    所有的结果可以根据控台更改,实时生效。

    实现原理

    底层依赖

    依赖 https://github.com/houbb/sensitive-word 提供的敏感词工具。

    因为原来的工具只有一些最基本的功能,无法根据配置动态变化,不符合实际应用场景。

    所以在原有的基础上实现了一个开箱即用的控台,并且提供了简单的 API。

    对于敏感词工具本身,本篇不做介绍,推荐阅读:

    敏感词使用入门介绍

    技术选型

    springboot

    mybatis-plus

    vue

    基于数据库的敏感词

    自定义的敏感词相关数据存储在数据库,对应的定义如下:

    /**
     * 自定义敏感词
     * 
     * @author 老马啸西风
     * @since 1.1.0
     */
    @Component
    public class MyDdWordDeny implements IWordDeny {
    
        @Autowired
        private WordService wordService;
    
        @Override
        public List<String> deny() {
            Wrapper<Word> wordWrapper = new EntityWrapper<>();
            wordWrapper.eq("type", WordTypeEnum.DENY.getCode());
            wordWrapper.eq("status", WordStatusEnum.S.getCode());
    
            List<Word> wordList = wordService.selectList(wordWrapper);
    
            return CollectionUtil.toList(wordList, new IHandler<Word, String>() {
                @Override
                public String handle(Word word) {
                    return word.getWord();
                }
            });
        }
    
    }
    

    /**
     * 自定义白名单
     * 
     * @author 老马啸西风
     * @since 1.1.0
     */
    @Component
    public class MyDdWordAllow implements IWordAllow {
    
        @Autowired
        private WordService wordService;
    
        @Override
        public List<String> allow() {
            Wrapper<Word> wordWrapper = new EntityWrapper<>();
            wordWrapper.eq("type", WordTypeEnum.ALLOW.getCode());
            wordWrapper.eq("status", WordStatusEnum.S.getCode());
    
            List<Word> wordList = wordService.selectList(wordWrapper);
    
            return CollectionUtil.toList(wordList, new IHandler<Word, String>() {
                @Override
                public String handle(Word word) {
                    return word.getWord();
                }
            });
        }
    
    }
    

    敏感词引导类初始化

    对应的敏感类初始化也比较简单,我们在系统默认的基础上,添加上自定义的数据。

    @Configuration
    public class SensitiveWordConfig {
    
        @Autowired
        private MyDdWordAllow myDdWordAllow;
    
        @Autowired
        private MyDdWordDeny myDdWordDeny;
    
        /**
         * 初始化引导类
         * @return 初始化引导类
         * @since 1.0.0
         */
        @Bean
        public SensitiveWordBs sensitiveWordBs() {
            return SensitiveWordBs.newInstance()
                    .wordAllow(WordAllows.chains(WordAllows.system(), myDdWordAllow))
                    .wordDeny(WordDenys.chains(WordDenys.system(), myDdWordDeny))
                    .ignoreRepeat(false)
                    // 各种其他配置
                    .init();
        }
    
    }
    

    配置变更及敏感词刷新

    每一次敏感词配置发生变更的时候,我们都主动刷新一下敏感词词典信息。

    /**
     * <p>
     * 敏感词表 前端控制器
     * </p>
     *
     * @author 老马啸西风
     * @since 2021-07-07
     */
    @Controller
    @RequestMapping("/word")
    @TraceId
    @AutoLog
    public class WordController {
    
        @Autowired
        private WordService wordService;
    
        @Autowired
        private SensitiveWordBs sensitiveWordBs;
    
        /**
        * 首页
        */
        @RequestMapping("/index")
        public String index() {
            return "word/index";
        }
    
        /**
        * 添加元素
        * @param entity 实体
        * @return 结果
        */
        @RequestMapping("/add")
        @ResponseBody
        public BaseResp add(@RequestBody final Word entity) {
            wordService.insert(entity);
    
            refreshSensitiveWord();
            return RespUtil.success();
        }
    
        /**
        * 编辑
        * @param entity 实体
        * @return 结果
        */
        @RequestMapping("/edit")
        @ResponseBody
        public BaseResp edit(final Word entity) {
            wordService.updateById(entity);
    
            refreshSensitiveWord();
            return RespUtil.success();
        }
    
        /**
        * 删除
        * @param id 实体
        * @return 结果
        */
        @RequestMapping("/remove/{id}")
        @ResponseBody
        public BaseResp remove(@PathVariable final Integer id) {
            wordService.deleteById(id);
    
            refreshSensitiveWord();
            return RespUtil.success();
        }
    
        /**
         * 刷新敏感詞
         *
         * 可以优化为异步,甚至批量。
         * @since 1.1.0
         */
        private void refreshSensitiveWord() {
            sensitiveWordBs.init();
        }
    
    }
    

    提供对外接口

    这里提供的接口仅当做演示:

    /**
     * api 服务
     * @author 老马啸西风
     * @since 1.1.0
     */
    @RestController
    @RequestMapping("/api/sensitiveWord/")
    @AutoLog
    @TraceId
    public class ApiSensitiveWordController {
    
        @Autowired
        private SensitiveWordBs sensitiveWordBs;
    
        /**
         * 是否包含敏感词
         *
         * @param text 文本
         * @return 结果
         */
        @RequestMapping("/contains")
        public BaseResp contains(@RequestParam("text") String text) {
           boolean contains = sensitiveWordBs.contains(text);
    
            return RespUtil.of(contains);
        }
    
        /**
         * 获取所有的敏感词
         * @param text 文本
         * @return 结果
         */
        @RequestMapping("/findAll")
        public BaseResp findAll(@RequestParam("text") String text) {
            List<String> results = sensitiveWordBs.findAll(text);
    
            return RespUtil.of(results);
        }
    
        /**
         * 获取替换后的结果
         *
         * @param text 文本
         * @return 结果
         */
        @RequestMapping("/replace")
        public BaseResp replace(@RequestParam("text") String text) {
            String results = sensitiveWordBs.replace(text);
    
            return RespUtil.of(results);
        }
    
    }
    

    如果实际我们真的对外部提供服务,肯定要比这个复杂的多,如果要考虑安全相关问题。

    推荐阅读:

    如何从零实现属于自己的 API 网关?

    你连对外接口签名都不会知道?有时间还是要学习学习

    和你一起走进对称加密算法的世界

    springboot 实现拦截器的 3 种方式介绍

    小结

    敏感词的应用非常广泛,任何涉及到用户可以自由发言的地方,就需要考虑敏感词。

    本文主要是为了演示如何基于 sensitive-word 实现一个开箱即用的敏感词服务,希望对你有所帮助。

    我是老马,期待与你的下次重逢。

    备注:涉及的代码较多,文中做了简化。若你对源码感兴趣,可以關註{老马啸西风},後臺回復{敏感词}即可获得。

    640.png

  • 相关阅读:
    反射机制小结
    IO流小节
    C# 利用file打印日志
    JS判断表单内容是否更改过
    input file 样式以及获取选择文件方法集合
    JS判断终端浏览器类型
    Node.js服务的重启与监控
    ASP.NET MVC 与Form表单交互
    JS 更改表单的提交时间和Input file的样式
    My SQL和LINQ 实现ROW_NUMBER() OVER以及Fatal error encountered during command execution
  • 原文地址:https://www.cnblogs.com/houbbBlogs/p/15037154.html
Copyright © 2011-2022 走看看