【概述】做好一个web系统的安全运维,除了常规的防注入,防入侵等,还有一个检测并过滤敏感词,脏词..  这件事做得不好,轻则导致一场投诉或纠纷,重则导致产品被勒令关闭停运。

废话少说,先看下代码,可以拿过去直接使用。

 using Microsoft.VisualBasic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text; namespace OpenCore.ContentSecurity
{
/// <summary>
/// 功能简介:基于DFA算法的高效率非法关键词检测过滤类(杜绝违法内容)..之所以高效,因为本算法对主输入的字符串,只循环了一次。 无需对词库的每个词进行replace的低效率处理。
/// 开发前参考内容:https://blog.csdn.net/u011966339/article/details/72832197
/// 更新日志:
/// 2020-4-15:加载字典的处理采用静态构造方法中处理,避免频繁加载,提升性能.
/// 支持多词库文件加载.
/// 优化了算法的细节,提高健壮性。
/// </summary>
public class SensitiveWordFilter
{
private static string[] dictionaryPathList = null;
/// <summary>
/// 内存词典
/// </summary>
private static WordGroup[] MEMORYLEXICON = new WordGroup[(int)char.MaxValue];
private static object lockObj = new object();
public static void Init(string[] sDictionaryFileName)
{
dictionaryPathList = sDictionaryFileName;
LoadDictionary();
}
public SensitiveWordFilter()
{ }
private string sourctText = string.Empty;
/// <summary>
/// 检测源
/// </summary>
private string SourctText
{
get { return sourctText; }
set { sourctText = value; }
}
/// <summary>
/// 检测源游标
/// </summary>
private int cursor = ;
/// <summary>
/// 匹配成功后偏移量
/// </summary>
private int wordlenght = ;
/// <summary>
/// 检测词游标
/// </summary>
private int nextCursor = ;
private List<string> illegalWords = new List<string>();
/// <summary>
/// 检测到的非法词集
/// </summary>
public List<string> IllegalWords
{
get { return illegalWords; }
}
/// <summary>
/// 判断是否是中文
/// </summary>
/// <param name="character"></param>
/// <returns></returns>
private bool isCHS(char character)
{
// 中文表意字符的范围 4E00-9FA5
int charVal = (int)character;
return (charVal >= 0x4e00 && charVal <= 0x9fa5);
}
/// <summary>
/// 判断是否是数字
/// </summary>
/// <param name="character"></param>
/// <returns></returns>
private bool isNum(char character)
{
int charVal = (int)character;
return (charVal >= && charVal <= );
}
/// <summary>
/// 判断是否是字母
/// </summary>
/// <param name="character"></param>
/// <returns></returns>
private bool isAlphabet(char character)
{
int charVal = (int)character;
return ((charVal >= && charVal <= ) || (charVal >= && charVal <= ));
}
/// <summary>
/// 转半角小写的函数(DBC case)
/// </summary>
/// <param name="input">任意字符串</param>
/// <returns>半角字符串</returns>
///<remarks>
///全角空格为12288,半角空格为32
///其他字符半角(33-126)与全角(65281-65374)的对应关系是:均相差65248
///</remarks>
private static string ToDBC(string input)
{
char[] c = input.ToCharArray();
for (int i = ; i < c.Length; i++)
{
if (c[i] == )
{
c[i] = (char);
continue;
}
if (c[i] > && c[i] < )
c[i] = (char)(c[i] - );
}
return new string(c).ToLower();
}
/// <summary>
/// 转换为简体中文
/// </summary>
/// <param name="sInput"></param>
/// <returns></returns>
private static string ToSimplifiedChiniese(string sInput)
{
if (string.IsNullOrEmpty(sInput))
{
return string.Empty;
}
try
{
return Strings.StrConv(sInput, VbStrConv.SimplifiedChinese, );
}
catch (Exception ex)
{ }
return sInput;
}
/// <summary>
/// 写入日志(非跨程序域的场景)
/// </summary>
/// <param name="Msg"></param>
private static void SaveLog(string Msg)
{
string sPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "SecurityLog");
if (!Directory.Exists(sPath))
{
Directory.CreateDirectory(sPath);
}
sPath = string.Format("{0}\\{1}", sPath, DateTime.Now.ToString("yyyyMMdd") + ".log");
try
{
File.AppendAllText(sPath, "[" + DateTime.Now.ToString() + "]" + Msg + "\r\n");
}
catch
{
}
}
/// <summary>
/// 加载内存词库
/// </summary>
private static void LoadDictionary()
{
if (dictionaryPathList == null || dictionaryPathList.Length == )
{
SaveLog($"SensitiveWordFilter.LoadDictionary.字典路径配置为空");
return;
}
foreach (string sFileName in dictionaryPathList)
{
if (File.Exists(sFileName) == false)
{
SaveLog($"SensitiveWordFilter.LoadDictionary.路径:{sFileName}不是一个有效的文件");
return;
}
}
List<string> wordList = new List<string>();
Array.Clear(MEMORYLEXICON, , MEMORYLEXICON.Length);
foreach (string sDictionaryFile in dictionaryPathList)
{
string[] words = System.IO.File.ReadAllLines(sDictionaryFile, System.Text.Encoding.Default);
foreach (string word in words)
{
if (string.IsNullOrEmpty(word))
continue;
if (word.Trim().Length == )
continue;
string key = ToDBC(word);
wordList.Add(key);
//适配繁体,简体.addbyww@2020-4-15
string key_simple = ToSimplifiedChiniese(key);
if (key_simple != key)
{
wordList.Add(key_simple);
}
}
}
Comparison<string> cmp = delegate (string key1, string key2)
{
return key1.CompareTo(key2);
};
wordList.Sort(cmp);
for (int i = wordList.Count - ; i > ; i--)
{
if (wordList[i].ToString() == wordList[i - ].ToString())
{
wordList.RemoveAt(i);
}
}
foreach (var word in wordList)
{
if (word.Length > )
{
WordGroup group = MEMORYLEXICON[(int)word[]];
if (group == null)
{
group = new WordGroup();
MEMORYLEXICON[(int)word[]] = group;
}
group.Add(word.Substring());
}
}
}
/// <summary>
/// 检测
/// </summary>
/// <param name="blackWord"></param>
/// <returns></returns>
private bool Check(string blackWord)
{
wordlenght = ;
//检测源下一位游标
nextCursor = cursor + ;
bool found = false;
//遍历词的每一位做匹配
for (int i = ; i < blackWord.Length; i++)
{
//特殊字符偏移游标
int offset = ;
if (nextCursor >= sourctText.Length)
{
break;
}
else
{
//检测下位字符如果不是汉字 数字 字符 偏移量加1
for (int y = nextCursor; y < sourctText.Length; y++)
{ if (!isCHS(sourctText[y]) && !isNum(sourctText[y]) && !isAlphabet(sourctText[y]))
{
offset++;
//避让特殊字符,下位游标如果>=字符串长度 跳出
if (nextCursor + offset >= sourctText.Length) break;
wordlenght++;
}
else break;
}
if ((int)blackWord[i] == (int)sourctText[nextCursor + offset])
{
found = true;
}
else
{
found = false;
break;
}
}
nextCursor = nextCursor + + offset;
wordlenght++;
}
return found;
}
/// <summary>
/// 检测并替换敏感词为指定字符。之后返回
/// </summary>
/// <param name="replaceChar">比如:*</param>
public string getDataByFilter(string sSourceInput, char replaceChar)
{
if (string.IsNullOrEmpty(sSourceInput))
{
return sSourceInput;
}
if (MEMORYLEXICON == null || MEMORYLEXICON.Length == )
{
SaveLog($"SensitiveWordFilter.getDataByFilter.内存字典为空");
return sSourceInput;
}
//初始化
this.cursor = ;
this.wordlenght = ;
this.illegalWords.Clear();
this.sourctText = sSourceInput;
if (sourctText != string.Empty)
{
char[] tempString = sourctText.ToCharArray();
for (int i = ; i < SourctText.Length; i++)
{
//查询以该字为首字符的词组
WordGroup group = MEMORYLEXICON[(int)ToDBC(SourctText)[i]];
if (group != null)
{
for (int z = ; z < group.Count(); z++)
{
string word = group.GetWord(z);
if (word.Length == || Check(word))
{
string blackword = string.Empty;
for (int pos = ; pos < wordlenght + ; pos++)
{
blackword += tempString[pos + cursor].ToString();
tempString[pos + cursor] = replaceChar;
}
illegalWords.Add(blackword);
cursor = cursor + wordlenght;
i = i + wordlenght;
}
}
}
cursor++;
}
return new string(tempString);
}
else
{
return string.Empty;
}
}
}
/// <summary>
/// 具有相同首字符的词组集合
/// </summary>
public class WordGroup
{
/// <summary>
/// 集合
/// </summary>
private List<string> groupList=new List<string>();
public WordGroup()
{ }
/// <summary>
/// 添加词
/// </summary>
/// <param name="word"></param>
public void Add(string word)
{
if (groupList.Contains(word) == false)
{
groupList.Add(word);
}
}
/// <summary>
/// 获取总数
/// </summary>
/// <returns></returns>
public int Count()
{
return groupList.Count;
}
/// <summary>
/// 根据下标获取词
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public string GetWord(int index)
{
return groupList[index];
}
}
}

上面是一个完整的,独立的实现类。 下面给一个简单的调用示例:

   //全局配置,整个程序只要配置一次即可,后续无需配置
SensitiveWordFilter.Init(new string[] {
@"C:\Users\x\Downloads\网站需要过滤的敏感词\mgck-master\暴恐词库.txt",
@"C:\Users\x\Downloads\网站需要过滤的敏感词\mgck-master\反动词库.txt",
@"C:\Users\x\Downloads\网站需要过滤的敏感词\mgck-master\民生词库.txt",
@"C:\Users\x\Downloads\网站需要过滤的敏感词\mgck-master\色情词库.txt",
@"C:\Users\x\Downloads\网站需要过滤的敏感词\mgck-master\贪腐词库.txt",
@"C:\Users\x\Downloads\网站需要过滤的敏感词\mgck-master\其他词库.txt"
});//注:这里的路径一定要写正确,否则本算法无法生效。
//下列可以在多个地方实例化,可以并发执行
SensitiveWordFilter wordFilter = new SensitiveWordFilter();
Dictionary<string, string> dictTestData = new Dictionary<string, string>();
//多测几个示例,看看效果
dictTestData["杀^人游戏,有人找一夜q"] = "";//注意,这里本来不是"一夜q",可惜咱们博客园本身也有敏感词检测,无法发布。所以改成q。 如果有人需要测试,请在本地改为词库里的一些内容。!!
dictTestData["数学学习课堂"] = "";
dictTestData["打击法0功有,法0功毒害大众"] = "";
Dictionary<string, string> dictResult = new Dictionary<string, string>();
foreach(string sKey in dictTestData.Keys)
{
dictResult[sKey] = $"替换后:{wordFilter.getDataByFilter(sKey,'|')}, ------------检测违禁词:{string.Join(",",(wordFilter.IllegalWords==null?new List<string>():wordFilter.IllegalWords))}";
}
string sResultJson = JsonConverter.SerializeObject(dictResult);
Utils.SaveLog(sResultJson);

 最后,给一下打印的结果:

"杀^人游戏,有人找一夜q":     替换后: "杀^人游戏,有人找|||", ------------检测违禁词:一夜q",  
"数学学习课堂":     替换后:"数学学习课堂", ------------检测违禁词:,
"打击法0功有,法0功毒害大众":   替换后:"打击|||有,|||毒害大众", ------------检测违禁词:法0功,法0功"

-------------附

词库下载地址:https://codeload.github.com/chason777777/mgck/zip/master

web系统安全运营之基础- 基于DFA算法的高性能的敏感词,脏词的检测过滤算法类(c#).的更多相关文章

  1. 基于DFA算法、RegExp对象和vee-validate实现前端敏感词过滤

    面临敏感词过滤的问题,最简单的方案就是对要检测的文本,遍历所有敏感词,逐个检测输入的文本是否包含指定的敏感词. 很明显上面这种实现方法的检测时间会随着敏感词库数量的增加而线性增加.系统会因此面临性能和 ...

  2. 基于Criminisi算法的栅格影像数据敏感地物隐藏

    栅格影像数据敏感地物伪装是指通过计算机智能识别与计算,将影像数据中的敏感地物进行识别与提取,将敏感地物智能替换成公共地物,如草地.森林.湖泊.公园等.但目前该技术并不成熟,同时栅格影像数据敏感地物伪装 ...

  3. 利用 DFA 算法实现文字过滤

    一.DEA 算法简介 在实现文字过滤的算法中,DFA是唯一比较好的实现算法. DFA 全称为:Deterministic Finite Automaton,即确定有穷自动机.其特征为:有一个有限状态集 ...

  4. Java实现敏感词过滤 - DFA算法

    Java实现DFA算法进行敏感词过滤 封装工具类如下: 使用前需对敏感词库进行初始化: SensitiveWordUtil.init(sensitiveWordSet); package cn.swf ...

  5. 基于.net EF6 MVC5+WEB Api 的Web系统框架总结(1)-Web前端页面

    本 Web 系统框架基于C# EF6+MVC+WebApi的快速应用开发平台.本节主要介绍Web前端页面设计与实现.Web前端页面主要分为普通列表页面.树状导航列表页面.普通编辑页面.数据导入页面.向 ...

  6. 基于Spring、SpringMVC、MyBatis、Druid、Shrio构建web系统

    源码下载地址:https://github.com/shuaijunlan/Autumn-Framework 在线Demo:http://autumn.shuaijunlan.cn 项目介绍 Autu ...

  7. 基于springboot搭建的web系统架构

    从接触springboot开始,便深深的被它的简洁性深深的折服了,精简的配置,方便的集成,使我再也不想用传统的ssm框架来搭建项目,一大堆的配置文件,维护起来很不方便,集成的时候也要费力不少.从第一次 ...

  8. 大型web系统数据缓存设计

    1. 前言 在高访问量的web系统中,缓存几乎是离不开的:但是一个适当.高效的缓存方案设计却并不容易:所以接下来将讨论一下应用系统缓存的设计方面应该注意哪些东西,包括缓存的选型.常见缓存系统的特点和数 ...

  9. 亿级 Web 系统的容错性建设实践

    一. 重试机制 最容易也最简单被人想到的容错方式,当然就是“失败重试”,总而言之,简单粗暴!简单是指它的实现通常很简单,粗暴则是指使用不当,很可能会带来系统“雪崩”的风险,因为重试意味着对后端服务的双 ...

随机推荐

  1. 爬虫前奏——初谈Requests库

    什么是Requests Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库如果你看过上篇文章关于urllib库的使用,你会发现,其 ...

  2. FastDFS源码学习(一)FastDFS介绍及源码编译安装

    FastDFS是淘宝的余庆主导开发的一个分布式文件系统,采用C语言开发,性能较优.在淘宝网.京东商城.支付宝和某些网盘等系统均有使用,使用场景十分广泛. 下图来源:https://blog.csdn. ...

  3. token iviewAdmin + php 登录验证解决方案

    思路: php 开启 Session 登录时 生成token,前端存下,然后每次走接口 验证下Session里的token和前端发过来的token是否一样. 遇到问题:后端 每次PHP Session ...

  4. win10安装ubuntu子系统和图形界面

    子系统可以很方便的调用windows的文件(在/mnt里就有各个盘),也可以在windows里用VScode编辑linux的文件.还是很方便的.也可以切出去用QQ微信. 安装子系统参考教程:https ...

  5. JMeter脚本拷贝自动化

    方法一:DOC命令拷贝脚本(适合Windows系统) 1.写一段DOC命令(保存为批处理文件copyscript.bat),将本地JMeter脚本拷贝到远程机器上. net use \\<远程机 ...

  6. vscode如何配置debug,python正则表达式如何匹配括号,关于python如何导入自定义模块

    关于vscode如何配置debug的问题: 1.下载安装好python,并且配置好 环境变量 2.https://www.cnblogs.com/asce/p/11600904.html 3.严格按照 ...

  7. Ruby中的Hash(哈希),你可以理解为字典

    原文链接 以下代码在Ruby 2.5.1中编译通过 定义 myHash = Hash.new myHash1 = Hash["key1" => 100, "key2 ...

  8. 告别炼丹,Google Brain提出强化学习助力Neural Architecture Search | ICLR2017

    论文为Google Brain在16年推出的使用强化学习的Neural Architecture Search方法,该方法能够针对数据集搜索构建特定的网络,但需要800卡训练一个月时间.虽然论文的思路 ...

  9. hdu1455 拼木棍(经典dfs)

    给定木棍序列,求解能将木棍拼成相同长度的数根长木棍的情况下长木棍长度的最小值. /*hdu1455dfs */ #include<bits/stdc++.h> using namespac ...

  10. Hadoop调度器

    一.FIFO调度器(先进先出调度) 上图为FIFO调度器的执行过程示意图.FIFO Scheduler是最简单也是最容易理解的调度器,它缺点是不适用于共享集群.大的应用可能会占用所有集群资源,这就导致 ...