
这几天博客都被垃圾评论困扰着,于是就有了一个写一个验证码的想法。这个验证码主要是增加机器识别的难度,当前网上各种流行的验证码中,感觉动网的验证码比较优秀,比较不容易被机器识别。这个验证码方法能够自动生成中文、数字、字母的混合字符串,并且自动设置有效的验证码值。上图中的有效验证码值为“mejjh”,而图片全文为“mej的jh”,这样就可以有效地防止被机器识别出验证码了。我把动网里的验证码代码剥离出来,并作了一些易用性修改。
这几天博客都被垃圾评论困扰着,于是就有了一个写一个验证码的想法。这个验证码主要是增加机器识别的难度,当前网上各种流行的验证码中,感觉动网的验证码比较优秀,比较不容易被机器识别。
从动网官网下载了动网.net 1.1版本,用Reflector反编译它的源代码。
请先看动网验证码的截图:

系统自动生成中文、数字、字母的混合字符串,并且自动设置有效的验证码值。上图中的有效验证码值为“mejjh”,而图片全文为“mej的jh”,这样就可以有效地防止被机器识别出验证码了。
我把动网里的验证码代码剥离出来,并作了一些修改。
在动网源代码中,它生成的验证码参数设置是读取Request.QueryString值的,这里我们不再使用这种方式设置验证码,改用xml文件保存设置。

XML配置
<?xml version="1.0" encoding="utf-8" ?>
<config>
<!--背景色-->
<bgcolor value="LightBlue"/>
<!--粗体-->
<bold value="true" />
<!--前景色-->
<forecolor value="Blue" />
<!--字体-->
<fontfamily value="Arial, Helvetica, sans-serif,宋体" />
<!--字体大小-->
<fontsize value="20" />
<!--图片高度-->
<height value="70" />
<!--杂质-->
<impurity value="2" />
<!--斜体-->
<italic value="false" />
<!--图片宽度-->
<width value="180" />
<!--验证码字符个数-->
<number value="6" />
<!--可用的验证码值-->
<codetext>
<![CDATA[我人有的和主产不为这工要在地一上是中经以发了民同国木子123456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ]]>
</codetext>
</config>
再把xml文件的配置信息映射到一个类上:

XML配置类

/**//// <summary>
/// 验证码配置文件
/// </summary>
public class ValidateCodeConfig


{

属性#region 属性
private Color bgcolor;

/**//// <summary>
/// 背景色
/// </summary>
public Color BgColor

{

get
{ return bgcolor; }

set
{ bgcolor = value; }
}
private bool bold;

/**//// <summary>
/// 是否粗体
/// </summary>
public bool Bold

{

get
{ return bold; }

set
{ bold = value; }
}
private Color forecolor;

/**//// <summary>
/// 颜色
/// </summary>
public Color ForeColor

{

get
{ return forecolor; }

set
{ forecolor = value; }
}
private string fontFamily;

/**//// <summary>
/// 字体
/// </summary>
public string FontFamily

{

get
{ return fontFamily; }

set
{ fontFamily = value; }
}
private int fontSize;

/**//// <summary>
/// 字体大小
/// </summary>
public int FontSize

{

get
{ return fontSize; }

set
{ fontSize = value; }
}
private int height;

/**//// <summary>
/// 高度
/// </summary>
public int Height

{

get
{ return height; }
set

{
if (300 < value)
height = 300;
else
height = value;
}
}
private int impurity;

/**//// <summary>
/// 杂质
/// </summary>
public int Impurity

{

get
{ return impurity; }
set

{
if (30 < value)
impurity = 30;
else
impurity = value;
}
}
private bool italic;

/**//// <summary>
/// 斜体
/// </summary>
public bool Italic

{

get
{ return italic; }

set
{ italic = value; }
}
private int width;

/**//// <summary>
/// 宽度
/// </summary>
public int Width

{

get
{ return width; }
set

{
if (300 < value)
width = 300;
else
width = value;
}
}
private char[] codetext;

/**//// <summary>
/// 验证码字符
/// </summary>
public char[] CodeText

{

get
{ return codetext; }

set
{ codetext = value; }
}
private int number;

/**//// <summary>
/// 验证码字数
/// </summary>
public int Number

{

get
{ return number; }

set
{ number = value; }
}
#endregion


/**//// <summary>
/// 构造函数
/// </summary>
public ValidateCodeConfig()

{
LoadXMLOnInit();
}


/**//// <summary>
/// 加载config.xml配置
/// </summary>
private void LoadXMLOnInit()

{
XmlDocument doc = new XmlDocument();
doc.Load(HttpContext.Current.Server.MapPath("~/ValidateCode/config.xml"));
this.bgcolor = Color.FromName(doc.GetElementsByTagName("bgcolor").Item(0).Attributes["value"].Value);
this.bold = Boolean.Parse(doc.GetElementsByTagName("bold").Item(0).Attributes["value"].Value);
this.codetext = doc.GetElementsByTagName("codetext").Item(0).InnerText.ToCharArray();
this.fontFamily = doc.GetElementsByTagName("fontfamily").Item(0).Attributes["value"].Value;
this.fontSize = int.Parse(doc.GetElementsByTagName("fontsize").Item(0).Attributes["value"].Value);
this.forecolor = Color.FromName(doc.GetElementsByTagName("forecolor").Item(0).Attributes["value"].Value);
this.height = int.Parse(doc.GetElementsByTagName("height").Item(0).Attributes["value"].Value);
this.impurity = int.Parse(doc.GetElementsByTagName("impurity").Item(0).Attributes["value"].Value);
this.italic = Boolean.Parse(doc.GetElementsByTagName("italic").Item(0).Attributes["value"].Value);
this.width = int.Parse(doc.GetElementsByTagName("width").Item(0).Attributes["value"].Value);
this.number = int.Parse(doc.GetElementsByTagName("number").Item(0).Attributes["value"].Value);
}
}
在绘图方法中调用这个类,并为其设置缓存依赖:

为配置文件设置缓存依赖

/**//// <summary>
/// 为配置文件设置缓存依赖
/// </summary>
private void LoadConfig()


{
if (HttpRuntime.Cache["ValidateCodeConfig"] == null)

{
config = new ValidateCodeConfig();
CacheDependency dep = new CacheDependency(HttpContext.Current.Server.MapPath("~/ValidateCode/config.xml"));
HttpRuntime.Cache.Insert("ValidateCodeConfig", config, dep, DateTime.MaxValue,
TimeSpan.Zero);
}
else

{
config = (ValidateCodeConfig)HttpRuntime.Cache["ValidateCodeConfig"];
}
}
之后,我们就可以开始写生成验证码的主要方法了:

验证码主要方法

/**//// <summary>
/// 验证码字体样式
/// </summary>
/// <returns></returns>
private FontStyle GetFontStyle()


{
if (config.Bold && config.Italic)

{
return (FontStyle.Italic | FontStyle.Bold);
}
if (config.Bold)

{
return FontStyle.Bold;
}
if (config.Italic)

{
return FontStyle.Italic;
}
return FontStyle.Regular;
}

private Bitmap GetneralCodeImage()


{
bool flag3;
Random random = new Random();
Bitmap image = new Bitmap(config.Width, config.Height);
Graphics graphics = Graphics.FromImage(image);
graphics.Clear(config.BgColor);
int length = Directory.GetFiles(Server.MapPath("~/ValidateCode/"), "*.gif").Length;
int backgroundfile = random.Next(1, length);

Brush brush = new TextureBrush(new Bitmap(Server.MapPath("~/ValidateCode/" + backgroundfile.ToString() + ".gif")));
Rectangle rect = new Rectangle(0, 0, config.Width, config.Height);
graphics.FillRectangle(brush, rect);
string[] strColorArray = new string[]


{
"#666666", "#003300", "#009900", "#330000", "#333300",
"#336600", "#660000", "#663300", "#666600", "#FF99FF",
"#FF33FF", "#CCCCFF", "#CC66FF", "#CC00FF", "#9999FF",
"#9933FF", "#9900FF", "#FF00FF", "#996633", "#CC3333",
"#FF3333", "#6633FF", "#0033FF", "#336666", "#CC9900",
"#6633CC", "#66FF66", "#009999", "#99CC00", "#615379",
"#E6649E", "#B5B495", "#73CEA7"
};
bool istop = random.Next(0, 2) == 0;
Pen pen = new Pen(ColorTranslator.FromHtml(strColorArray[random.Next(0, strColorArray.Length)]), 2f);
pen.DashStyle = DashStyle.Solid;
Point[] randomPointGroup = this.GetRandomPointGroup(istop);
for (int i = 0; i < randomPointGroup.Length; i += 2)

{
graphics.DrawLine(pen, randomPointGroup[i], randomPointGroup[i + 1]);
}
string input = "";
for (int j = 0; j < config.Number; j++)

{
input += config.CodeText[random.Next(config.CodeText.Length)].ToString();
}
string[] strArray2 = new string[config.Number + 1];
int index = 0;
Label_0313:
while (index != config.Number)

{
string str2 = strColorArray[random.Next(0, strColorArray.Length)];
strArray2[index] = str2;
index++;
for (int n = 0; n < strArray2.Length; n++)

{
if (strArray2[n] == str2)

{
goto Label_0313;
}
if (n == (strArray2.Length - 1))

{
strArray2[index] = str2;
index++;
}
}
}
char[] chArray2 = input.ToCharArray();
int num8 = random.Next(0, config.Number - 1);
string str3 = strArray2[num8];
int num9 = random.Next(0, config.Number - 2) + 1;
num9 = (num9 == 1) ? 2 : num9;
int[] numArray = new int[num9];
numArray[0] = num8;
for (int k = 1; k < num9; k++)

{
int num11 = random.Next(0, config.Number - 1);
bool flag2 = false;
for (int num12 = 0; num12 < k; num12++)

{
if (numArray[num12] == num11)

{
flag2 = true;
break;
}
}
if (!flag2)

{
numArray[k] = num11;
}
else

{
k--;
}
}
do

{
string str4 = strColorArray[random.Next(0, strColorArray.Length)];
flag3 = false;
for (int num13 = 0; num13 < strArray2.Length; num13++)

{
if (str4 == strArray2[num13])

{
flag3 = true;
break;
}
}
}
while (flag3);
string strRegexString = "";
string strRegexValidateType = "";

int intValidateType = random.Next(0, 3);

bool flag4 = false;
while (!flag4)

{
Match matchChinese;
Match matchLetter;
Match matchNumber;
switch (intValidateType)

{
case 0:
if (!Regex.IsMatch(input, @"[\u4e00-\u9fa5]+"))

{
goto Label_RandomCodeType;
}
flag4 = true;
strRegexValidateType = "中文字体";
matchChinese = Regex.Match(input, @"[\u4e00-\u9fa5]+", RegexOptions.Multiline | RegexOptions.IgnoreCase);
goto Label_ChineseRegexResult;

case 1:
if (!Regex.IsMatch(input, "[a-zA-Z]+"))

{
goto Label_RandomCodeType;
}
flag4 = true;
strRegexValidateType = "英文字母";
matchLetter = Regex.Match(input, "[a-zA-Z]+", RegexOptions.Multiline | RegexOptions.IgnoreCase);
goto Label_LetterRegexResult;

case 2:
if (!Regex.IsMatch(input, "[0-9]+"))

{
goto Label_RandomCodeType;
}
flag4 = true;
strRegexValidateType = "数字";
matchNumber = Regex.Match(input, "[0-9]+", RegexOptions.Multiline | RegexOptions.IgnoreCase);
goto Label_NumberRegexResult;

case 3:
if (Regex.IsMatch(input, @"[^\u4e00-\u9fa5|a-zA-Z|0-9]"))

{
flag4 = true;
strRegexValidateType = "特殊字符";
strRegexString = new Regex(@"[\u4e00-\u9fa5|a-zA-Z|0-9]+").Replace(input, "");
}
goto Label_RandomCodeType;

default:
goto Label_RandomCodeType;
}
Label_ChineseRegexSuccess:
strRegexString = strRegexString + matchChinese.Value;
matchChinese = matchChinese.NextMatch();
Label_ChineseRegexResult:
if (matchChinese.Success)

{
goto Label_ChineseRegexSuccess;
}
goto Label_RandomCodeType;
Label_LetterRegexSuccess:
strRegexString = strRegexString + matchLetter.Value;
matchLetter = matchLetter.NextMatch();
Label_LetterRegexResult:
if (matchLetter.Success)

{
goto Label_LetterRegexSuccess;
}
goto Label_RandomCodeType;
Label_NumberRegexSuccess:
strRegexString = strRegexString + matchNumber.Value;
matchNumber = matchNumber.NextMatch();
Label_NumberRegexResult:
if (matchNumber.Success)

{
goto Label_NumberRegexSuccess;
}

Label_RandomCodeType:
if (!flag4)

{
intValidateType = random.Next(0, 3);
}
}
int num16 = 0;
for (int m = 0; m < chArray2.Length; m++)

{
int num21;
int num18 = config.FontSize;
int num19 = (m == 0) ? 10 : 25;
num16 += num19;
int num20 = num16;
if (istop)

{
num21 = random.Next(15, config.Height - 35);
}
else

{
num21 = random.Next(0, config.Height - 45);
}
bool flag5 = false;
if ((((('.' != chArray2[m]) && ('_' != chArray2[m])) && (('*' != chArray2[m]) && ('`' != chArray2[m]))) && ((('~' != chArray2[m]) && ('.' != chArray2[m])) && ((',' != chArray2[m]) && ('\'' != chArray2[m])))) && ((('-' != chArray2[m]) && ('|' != chArray2[m])) && ('\\' != chArray2[m])))

{
char ch1 = chArray2[m];
}
graphics.DrawString(chArray2[m].ToString(), new Font(config.FontFamily, flag5 ? ((float)(num18 + 30)) : ((float)num18), flag5 ? FontStyle.Bold : this.GetFontStyle()), new SolidBrush(ColorTranslator.FromHtml(strArray2[m])), (float)num20, (float)num21);
}

graphics.DrawString("输入图中出现的", new Font("Verdana", 10f, FontStyle.Regular), new SolidBrush(ColorTranslator.FromHtml("#008000")), (2 == intValidateType) ? ((float)18) : ((float)4), istop ? ((float)2) : ((float)(config.Height - 16)));
graphics.DrawString(strRegexValidateType, new Font("Verdana", 10f, FontStyle.Bold), new SolidBrush(ColorTranslator.FromHtml("#FF0000")), (2 == intValidateType) ? ((float)126) : ((float)110), istop ? ((float)2) : ((float)(config.Height - 16)));

graphics.Dispose();
Response.Cookies.Add(new HttpCookie("ValidateCode", strRegexString.ToLower()));
return image;
}


/**//// <summary>
/// 杂质
/// </summary>
/// <param name="istop"></param>
/// <returns></returns>
private Point[] GetRandomPointGroup(bool istop)


{
Point[] pointArray = new Point[config.Impurity * 2];
for (int i = 0; i < pointArray.Length; i++)

{
int num2 = new Random().Next(10000000, 99999999);
int x = new Random((num2 / (i + 2)) * (i + 1)).Next(0, config.Width);
int y = new Random((num2 / (x + 2)) * (x + 1)).Next(istop ? 16 : 0, istop ? config.Height : (config.Height - 16));
pointArray[i] = new Point(x, y);
}
return pointArray;
}
最后,在Page_Load中调用上面的方法:
private void Page_Load(object sender, EventArgs e)


{
this.LoadConfig();
base.Response.ContentType = "image/gif";
this.GetneralCodeImage().Save(base.Response.OutputStream, ImageFormat.Gif);
}
演示效果如图:

DEMO源码下载:http://www.box.net/shared/g324d08ivy
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述