服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 12306奇葩验证码引发思考之C#实现验证码程序

12306奇葩验证码引发思考之C#实现验证码程序

2021-11-05 10:57C#教程网 C#

春运最高峰来了!明天通过网络将能买到小年夜的车票,本周四就将开售除夕日车票,但不少人被首次在春运期间使用的图片验证码搞得很火大,小编也正在对验证码进行研究,编写了由C#实现验证码程序,分享给大家

近日铁路订票网“12306”又出现多道另类考题,竟要订票者在8个图案中“点击图中所有美男子”、“请点击下图中所有的非智能眼镜”、“请点击下图中所有的博斯普鲁斯海峡”,网友吐槽:比高考题还难,到底是什么样子的,先跟大家分享一下几个例子:

12306奇葩验证码引发思考之C#实现验证码程序

哈哈,是有点奇葩的验证码,怪不得有人会说“妈妈我已经找不到回家”,这让分秒必争的春运网上抢票者瞬间傻眼,九成网友已经被打败……

正巧小编最近也在研究验证码,参考了许多网上案例,整理了一篇文章特分享给大家。

验证码的一般编写思路为:
       1.定义验证码字符长度;
       2.根据长度随机生成验证码字符串;
       3.将验证码字符串转换成图片形式,并在图片中生成随机噪声点和声线(对验证码进行模糊识别处理);
       4.显示结果。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
///
 /// 生成随机验证码
 ///
 /// 验证码长度
 ///
 public string CreateIdentifyingCode(int CodeLen)
 {
 if (CodeLen < 1)
  return String.Empty;
 int num;
 string checkcode = String.Empty;
 Random random = new Random();
 for (int index = 0; index < CodeLen; index++)
 {
  num = random.Next();
  if (num % 2 == 0)
  checkcode += (char)('0' + (char)(num % 10));
  else
  checkcode += (char)('A' + (char)(num % 26));
 }
 return checkcode;
 }
-------------------------------------------------------------------------------------------------
 ///
 /// 生成验证码图片
 ///
 ///
 ///
 private void CreateCheckCodeImage(string checkcode)
 {
  if (checkcode == null || checkcode.Trim() == String.Empty)
  return;
  //创建图片大小
  System.Drawing.Bitmap image = new  System.Drawing.Bitmap((int)Math.Ceiling(checkcode.Length*12.5),22);
  //创建画板
  Graphics graphic = Graphics.FromImage(image);
 
  try
  {
  Random random = new Random();
  graphic.Clear(Color.White);
  int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
  //画图片背景噪声线
  for (int index = 0; index < 25; index++)
  {
   x1 = random.Next(image.Width);
   y1 = random.Next(image.Height);
   x2 = random.Next(image.Width);
   y2 = random.Next(image.Height);
   graphic.DrawLine(new Pen(Color.Silver),x1,y1,x2,y2);
  }
  Font font = new System.Drawing.Font("Helvetica", 12, (FontStyle.Bold |FontStyle.Italic));
  LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height),Color.Blue,Color.DarkBlue,1.2f,true);
  graphic.DrawString(checkcode,font,brush,2,2);
 
  int x = 0;
  int y = 0;
  // 画图片的前景噪声点
  for (int index = 0; index < 100; index++)
  {
   x = random.Next(image.Width);
   y = random.Next(image.Height);
   image.SetPixel(x,y,Color.FromArgb(random.Next()));
  }
  //画图片的边框线
  graphic.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
  //网页响应
  System.IO.MemoryStream ms = new System.IO.MemoryStream();
  image.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
  Response.ClearContent();
  Response.ContentType = "image/Gif";
  Response.BinaryWrite(ms.ToArray());
  }
  finally
  {
  graphic.Dispose();
  image.Dispose();
  }
 }

以上所生成的为简单的验证码。接下来我从其他的博客中学习了其他形式的效果。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 图片画线特殊效果:贝塞尔曲线 */
  Graphics graph = Graphics.FromImage(image);
  graph.Clear(Color.WhiteSmoke);
  Point[] myArray ={
     new Point(random.Next(150),random.Next(50)),
     new Point(random.Next(150),random.Next(50)),
     new Point(random.Next(150),random.Next(50)),
     new Point(random.Next(150),random.Next(50)),
     new Point(random.Next(150),random.Next(50)),
     new Point(random.Next(150),random.Next(50)),
     new Point(random.Next(150),random.Next(50)),
     new Point(random.Next(150),random.Next(50)),
     new Point(random.Next(150),random.Next(50)),
     new Point(random.Next(150),random.Next(50))
    };
  Pen myPen = new Pen(Color.Blue, 1);
  GraphicsPath myPath = new GraphicsPath();
  myPath.AddBeziers(myArray);
  graph.DrawPath(myPen, myPath);

验证码字符颜色变换效果:实现该效果,我们首先来定义一个颜色集合,然后通过for循环使其随机改变字体颜色则可。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#region 定义颜色数组
Color[] colors = { Color.Blue, Color.Green, Color.Red, Color.Gold, Color.Black, Color.Chocolate, Color.Orange, Color.Purple };
public Color[] Colors
{
 get { return colors; }
 set { colors = value; }
}
#endregion
Brush brush;
int colornum;
for(int i=0; i
{
 colornum = random.Next(Colors.Length - 1);
 brush = new System.Drawing.SolidBrush(Colors[cindex]);
 
 //利用DrawString函数进行颜色填充就可以了。
}

 同样的原理我们也可以定义一个字体的数组来进行验证码字体切换。代码和颜色的类似,这里就不加以累赘。
接下来看看如何使得验证码的字体进行扭曲。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private const double PI = 3.1415926535897932384626433832795;
 private const double PI2 = 6.283185307179586476925286766559;
 ///
 /// 波形滤镜效果函数
 ///
 ///
 ///
 ///
 ///
 ///
 public System.Drawing.Bitmap TwistImage(Bitmap srcbmp, double dmultvalue, double dphase)
 {
  System.Drawing.Bitmap destbmp = new Bitmap(srcbmp.Width,srcbmp.Height);
  System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(destbmp);
  //填充背景图为白色
  graph.FillRectangle(new SolidBrush(System.Drawing.Color.White), 0, 0, destbmp.Width, destbmp.Height);
  graph.Dispose();
  double dbaselen = (double)destbmp.Width;
  for (int i = 0; i < destbmp.Width; i++)
  {
  for (int j = 0; j < destbmp.Height; j++)
  {
   double dx = 0;
   dx = (PI2 * (double)j) / dbaselen;
   dx += dphase;
 
   double dy = Math.Sin(dx);
 
   int noldx = 0, noldy = 0;
   noldx = i + (int)(dy * dmultvalue);
   noldy = j + (int)(dy * dmultvalue);
   System.Drawing.Color color = srcbmp.GetPixel(i, j);
   if (noldx >= 0 && noldx < destbmp.Width && noldy >= 0 && noldy < destbmp.Height)
   destbmp.SetPixel(noldx, noldy, color);
  }
  }
  return destbmp;
 }

上面代码是参考了这段代码进行的学习<C#.net 好用的验证码代码 汉字-变色-扭曲-波动 >,代码如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Drawing;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
 
public partial class study_CheckCode2 : System.Web.UI.Page
{
 protected void Page_Load(object sender, EventArgs e)
 {
 string code = CreateVerifyCode();  //取随机码
 CreateImageOnPage(code, this.Context); // 输出图片
 Response.Cookies.Add(new HttpCookie("CheckCode", code.ToUpper()));// 使用Cookies取验证码的值
 }
 
 #region 验证码长度(默认4个验证码的长度)
 int length = 4;
 public int Length
 {
 get { return length; }
 set { length = value; }
 }
 #endregion
 
 #region 验证码字体大小(为了显示扭曲效果,默认40像素,可以自行修改)
 int fontSize = 40;
 public int FontSize
 {
 get { return fontSize; }
 set { fontSize = value; }
 }
 #endregion
 
 #region 边框补(默认1像素)
 int padding = 2;
 public int Padding
 {
 get { return padding; }
 set { padding = value; }
 }
 #endregion
 
 #region 是否输出燥点(默认不输出)
 bool chaos = true;
 public bool Chaos
 {
 get { return chaos; }
 set { chaos = value; }
 }
 #endregion
 
 #region 输出燥点的颜色(默认灰色)
 Color chaosColor = Color.LightGray;
 public Color ChaosColor
 {
 get { return chaosColor; }
 set { chaosColor = value; }
 }
 #endregion
 
 #region 自定义背景色(默认白色)
 Color backgroundColor = Color.White;
 public Color BackgroundColor
 {
 get { return backgroundColor; }
 set { backgroundColor = value; }
 }
 #endregion
 
 #region 自定义随机颜色数组
 Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
 public Color[] Colors
 {
 get { return colors; }
 set { colors = value; }
 }
 #endregion
 
 #region 自定义字体数组
 string[] fonts = { "Arial", "Georgia" };
 public string[] Fonts
 {
 get { return fonts; }
 set { fonts = value; }
 }
 #endregion
 
 #region 自定义随机码字符串序列(使用逗号分隔)
 //string codeSerial = "0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z";
 string codeSerial = "阿,保,持,的,法,规,和,东,三,省,问,我,惹,你,诶,没,改,变,揍,屁,股,吧";
 public string CodeSerial
 {
 get { return codeSerial; }
 set { codeSerial = value; }
 }
 #endregion
 
 #region 产生波形滤镜效果
 
 private const double PI = 3.1415926535897932384626433832795;
 private const double PI2 = 6.283185307179586476925286766559;
 
 /// <summary>
 /// 正弦曲线Wave扭曲图片(Edit By 51aspx.com)
 /// </summary>
 /// <param name="srcBmp">图片路径</param>
 /// <param name="bXDir">如果扭曲则选择为True</param>
 /// <param name="dMultValue">波形的幅度倍数,越大扭曲的程度越高,一般为3</param>
 /// <param name="dPhase">波形的起始相位,取值区间[0-2*PI)</param>
 /// <returns></returns>
 public System.Drawing.Bitmap TwistImage(Bitmap srcBmp, bool bXDir, double dMultValue, double dPhase)
 {
 System.Drawing.Bitmap destBmp = new Bitmap(srcBmp.Width, srcBmp.Height);
 
 // 将位图背景填充为白色
 System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(destBmp);
 graph.FillRectangle(new SolidBrush(System.Drawing.Color.White), 0, 0, destBmp.Width, destBmp.Height);
 graph.Dispose();
 
 double dBaseAxisLen = bXDir ? (double)destBmp.Height : (double)destBmp.Width;
 
 for (int i = 0; i < destBmp.Width; i++)
 {
  for (int j = 0; j < destBmp.Height; j++)
  {
  double dx = 0;
  dx = bXDir ? (PI2 * (double)j) / dBaseAxisLen : (PI2 * (double)i) / dBaseAxisLen;
  dx += dPhase;
  double dy = Math.Sin(dx);
 
  // 取得当前点的颜色
  int nOldX = 0, nOldY = 0;
  nOldX = bXDir ? i + (int)(dy * dMultValue) : i;
  nOldY = bXDir ? j : j + (int)(dy * dMultValue);
 
  System.Drawing.Color color = srcBmp.GetPixel(i, j);
  if (nOldX >= 0 && nOldX < destBmp.Width
   && nOldY >= 0 && nOldY < destBmp.Height)
  {
   destBmp.SetPixel(nOldX, nOldY, color);
  }
  }
 }
 return destBmp;
 }
 #endregion
 
 #region 生成校验码图片
 public Bitmap CreateImageCode(string code)
 {
 int fSize = FontSize;
 int fWidth = fSize + Padding;
 
 int imageWidth = (int)(code.Length * fWidth) + 30 + Padding * 2;
 int imageHeight = fSize * 2 + Padding;
 
 System.Drawing.Bitmap image = new System.Drawing.Bitmap(imageWidth, imageHeight);
 
 Graphics g = Graphics.FromImage(image);
 
 g.Clear(BackgroundColor);
 
 Random rand = new Random();
 
 //给背景添加随机生成的燥点
 if (this.Chaos)
 {
 
  Pen pen = new Pen(ChaosColor, 0);
  int c = Length * 10;
 
  for (int i = 0; i < c; i++)
  {
  int x = rand.Next(image.Width);
  int y = rand.Next(image.Height);
 
  g.DrawRectangle(pen, x, y, 1, 1);
  }
 }
 
 int left = 0, top = 0, top1 = 1, top2 = 1;
 
 int n1 = (imageHeight - FontSize - Padding * 2);
 int n2 = n1 / 4;
 top1 = n2;
 top2 = n2 * 2;
 
 Font f;
 Brush b;
 
 int cindex, findex;
 
 //随机字体和颜色的验证码字符
 for (int i = 0; i < code.Length; i++)
 {
  cindex = rand.Next(Colors.Length - 1);
  findex = rand.Next(Fonts.Length - 1);
 
  f = new System.Drawing.Font(Fonts[findex], fSize, System.Drawing.FontStyle.Bold);
  b = new System.Drawing.SolidBrush(Colors[cindex]);
 
  if (i % 2 == 1)
  {
  top = top2;
  }
  else
  {
  top = top1;
  }
 
  left = i * fWidth;
 
  g.DrawString(code.Substring(i, 1), f, b, left, top);
 }
 
 //画一个边框 边框颜色为Color.Gainsboro
 g.DrawRectangle(new Pen(Color.Gainsboro, 0), 0, 0, image.Width - 1, image.Height - 1);
 g.Dispose();
 
 //产生波形(Add By 51aspx.com)
 image = TwistImage(image, true, 8, 4);
 
 return image;
 }
 #endregion
 
 #region 将创建好的图片输出到页面
 public void CreateImageOnPage(string code, HttpContext context)
 {
 System.IO.MemoryStream ms = new System.IO.MemoryStream();
 Bitmap image = this.CreateImageCode(code);
 
 image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
 
 context.Response.ClearContent();
 context.Response.ContentType = "image/Jpeg";
 context.Response.BinaryWrite(ms.GetBuffer());
 
 ms.Close();
 ms = null;
 image.Dispose();
 image = null;
 }
 #endregion
 
 #region 生成随机字符码
 public string CreateVerifyCode(int codeLen)
 {
 if (codeLen == 0)
 {
  codeLen = Length;
 }
 
 string[] arr = CodeSerial.Split(',');
 
 string code = "";
 
 int randValue = -1;
 
 Random rand = new Random(unchecked((int)DateTime.Now.Ticks));
 
 for (int i = 0; i < codeLen; i++)
 {
  randValue = rand.Next(0, arr.Length - 1);
 
  code += arr[randValue];
 }
 
 return code;
 }
 public string CreateVerifyCode()
 {
 return CreateVerifyCode(0);
 }
 #endregion
 
}

一年一度的抢票热潮又开始了,希望大家都能顺利买到回家的火车篇,回家过年,突然感觉有点年味了,一年又一年,时间都去哪了,小小的感慨一下……

言归正传,这就是为大家分享的C#验证码程序,和12306验证码差多了,不过也是小编的学习收获吧!大家也可以结合下面这两篇文章进行学习:

《12306动态验证码启发之ASP.NET实现动态GIF验证码(附源码)》

《12306验证码破解思路分享》

希望本文所述对大家学习验证码技术有所帮助。

延伸 · 阅读

精彩推荐
  • C#C#实现的文件操作封装类完整实例【删除,移动,复制,重命名】

    C#实现的文件操作封装类完整实例【删除,移动,复制,重命名】

    这篇文章主要介绍了C#实现的文件操作封装类,结合完整实例形式分析了C#封装文件的删除,移动,复制,重命名等操作相关实现技巧,需要的朋友可以参考下...

    Rising_Sun3892021-12-28
  • C#c#学习之30分钟学会XAML

    c#学习之30分钟学会XAML

    一个界面程序的核心,无疑就是界面和后台代码,而xaml就是微软为构建应用程序界面而创建的一种描述性语言,也就是说,这东西是搞界面的...

    C#教程网8812021-12-10
  • C#Unity3D UGUI实现缩放循环拖动卡牌展示效果

    Unity3D UGUI实现缩放循环拖动卡牌展示效果

    这篇文章主要为大家详细介绍了Unity3D UGUI实现缩放循环拖动展示卡牌效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参...

    诗远3662022-03-11
  • C#C# 后台处理图片的几种方法

    C# 后台处理图片的几种方法

    本篇文章主要介绍了C# 后台处理图片的几种方法,非常具有实用价值,需要的朋友可以参考下。...

    IT小伙儿10162021-12-08
  • C#浅谈C# winForm 窗体闪烁的问题

    浅谈C# winForm 窗体闪烁的问题

    下面小编就为大家带来一篇浅谈C# winForm 窗体闪烁的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网7962021-12-21
  • C#聊一聊C#接口问题 新手速来围观

    聊一聊C#接口问题 新手速来围观

    聊一聊C#接口问题,新手速来围观,一个通俗易懂的例子帮助大家更好的理解C#接口问题,感兴趣的小伙伴们可以参考一下...

    zenkey7072021-12-03
  • C#C#基础之泛型

    C#基础之泛型

    泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能。接下来通过本文给大家介绍c#基础之泛型,感兴趣的朋友一起学习吧...

    方小白7732021-12-03
  • C#C#直线的最小二乘法线性回归运算实例

    C#直线的最小二乘法线性回归运算实例

    这篇文章主要介绍了C#直线的最小二乘法线性回归运算方法,实例分析了给定一组点,用最小二乘法进行线性回归运算的实现技巧,具有一定参考借鉴价值,需要...

    北风其凉8912021-10-18