本次和大家分享的是一個集成1:小寫拼音 2:大寫拼音 3:數字 4:漢字的驗證碼生成類,從標題來看感覺很普通的樣子,沒錯的確很普通,只是這個驗證碼類生成的時候可以通過參數指定驗證碼返回格式的規則,更主要的是希望能給大家帶來一定的實用性,本章例子也會有一個mvc使用驗證碼校驗的場景,希望大家能夠喜歡,也希望各位多多"掃碼支持"和"推薦"謝謝:
» 驗證碼生成流程圖
» 驗證碼生成池代碼的解析
» 把驗證代碼畫到圖片上
» mvc登錄操作測試驗證碼正確性
下面一步一個腳印的來分享:
» 驗證碼生成流程圖
首先,咋們來看一下本次分享的驗證碼生成類的生成流程圖:

能看到此圖描述的編碼生成池對應的是幾個不同的編碼內容,這裡主要根據參數設置允許同時獲取不同編碼內容,從而到達文字,拼音,漢字組合而成驗證碼,具體規則設置由參數而定;
» 驗證碼生成池代碼的解析
首先,由上面流程圖分析的內容能看出,這個驗證碼生成池子需要並行獲取不同類型驗證碼數據,才能滿足組合的驗證碼,因此有了下面的代碼:
1 /// <summary>
2 /// 創建驗證碼
3 /// </summary>
4 /// <param name="codeType">1:小寫拼音 2:大寫拼音 3:數字 4:漢字</param>
5 /// <returns></returns>
6 public static string CreateCode(string codeType = "1|2|3|4")
7 {
8 var code = string.Empty;
9 try
10 {
11 if (string.IsNullOrWhiteSpace(codeType) || codeType.IndexOf('|') < 0) { codeType = "1|2|3|4"; }
12 var codeTypeArr = codeType.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
13 var strLen = codeTypeArr.Length;
14
15 //任務
16 Task<string>[] taskArr = new Task<string>[strLen];
17 for (int i = 0; i < strLen; i++)
18 {
19 var val = codeTypeArr[i];
20 switch (val)
21 {
22 case "1": //小寫拼音
23 taskArr[i] = Task.Factory.StartNew<string>(() => { return GetPinYinOrUpper(false); });
24 break;
25 case "2": //大寫拼音
26 taskArr[i] = Task.Factory.StartNew<string>(() => { return GetPinYinOrUpper(); });
27 break;
28 case "3": //數字
29 taskArr[i] = Task.Factory.StartNew<string>(() => { return GetShuZi(); });
30 break;
31 case "4": //漢字
32 taskArr[i] = Task.Factory.StartNew<string>(() => { return GetHanZi(); });
33 break;
34 default:
35 break;
36 }
37 }
38
39 //等待完成 30s
40 Task.WaitAll(taskArr, TimeSpan.FromSeconds(30));
41
42 foreach (var item in taskArr)
43 {
44 code += item.Result;
45 }
46 }
47 catch (Exception ex)
48 {
49 code = "我愛祖國";
50 }
51 return code;
52 }
這裡繼續使用了關鍵字Task,來分發任務獲取不同的驗證碼內容,個人認為最主要的還是通過參數設置 string codeType = "1|2|3|4" ,來確定驗證碼的組合方式,這樣也達到了驗證碼格式的多樣性;
» 把驗證代碼畫到圖片上
首先,咋們要明確的是要吧文字畫在某個圖片上,那麼需要用到Graphics關鍵字,以此來創建畫布把咋們的驗證編碼畫到圖片上,這裡先上代碼:
1 /// <summary>
2 /// 生成驗證碼圖片流
3 /// </summary>
4 /// <param name="code">驗證碼文字</param>
5 /// <returns>流</returns>
6 public static byte[] CreateValidateCodeStream(string code = "我愛祖國", int fontSize = 18, int width = 0, int height = 0, string fontFamily = "華文楷體")
7 {
8 var bb = new byte[0];
9 //初始化畫布
10 var padding = 2;
11 var len = code.Length;
12 width = width <= 0 ? fontSize * 2 * (len - 1) + padding * 4 : width;
13 height = height <= 0 ? fontSize * 2 : height;
14 var image = new Bitmap(width, height);
15 var g = Graphics.FromImage(image);
16 try
17 {
18 var random = new Random();
19 //清空背景色
20 g.Clear(Color.White);
21 //畫橫向中間干擾線
22 var x1 = 0;
23 var y1 = height / 2;
24 var x2 = width;
25 var y2 = y1;
26 g.DrawLine(new Pen(Color.DarkRed), x1, y1, x2, y2);
27 //字體
28 var font = new Font(fontFamily, fontSize, (FontStyle.Bold | FontStyle.Italic));
29 var brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height),
30 Color.Blue, Color.DarkRed, 1f, true);
31 //畫文字
32 var stringFomart = new StringFormat();
33 //垂直居中
34 stringFomart.LineAlignment = StringAlignment.Center;
35 //水平居中
36 stringFomart.Alignment = StringAlignment.Center;
37 var rf = new Rectangle(Point.Empty, new Size(width, height));
38 g.DrawString(code, font, brush, rf, stringFomart);
39 //畫圖片的前景干擾點
40 for (int i = 0; i < 100; i++)
41 {
42 var x = random.Next(image.Width);
43 var y = random.Next(image.Height);
44 image.SetPixel(x, y, Color.FromArgb(random.Next()));
45 }
46 //畫圖片的邊框線
47 g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
48
49 //保存圖片流
50 var stream = new MemoryStream();
51 image.Save(stream, ImageFormat.Jpeg);
52 //輸出圖片流
53 bb = stream.ToArray();
54 }
55 catch (Exception ex) { }
56 finally
57 {
58 g.Dispose();
59 image.Dispose();
60 }
61 return bb;
62 }
這個列出畫驗證碼圖片方法的關鍵點:
1. 圖片的高和寬度需要設置,這個根據不同頁面布局方式而定,所以這裡吧高和寬用作參數傳遞
2. 干擾線:通常驗證碼圖片都以一兩條干擾線,主要防止某些惡意用戶使用圖片識別軟件進行不正規破解請求,我這裡干擾線只設置了橫向居中的一天直線代碼如: g.DrawLine(new Pen(Color.DarkRed), x1, y1, x2, y2);
3. 字體:一個好看的字體通常也一種用戶體驗,因此這裡根據需要參數傳遞字體;
4. 驗證代碼位於圖片縱橫向居中,這裡的關鍵代碼是:
1 var stringFomart = new StringFormat(); 2 //垂直居中 3 stringFomart.LineAlignment = StringAlignment.Center; 4 //水平居中 5 stringFomart.Alignment = StringAlignment.Center;
5. g.DrawString(code, font, brush, rf, stringFomart); 主要用來把文字畫到圖片上,這是最關鍵的地方
6. 咋們通常都是吧驗證碼弄成圖片流,而不是真的生成一個實體的驗證碼圖片保存到服務器上,不然這樣服務器很快就會磁盤不足,所以
1 //保存圖片流 2 var stream = new MemoryStream(); 3 image.Save(stream, ImageFormat.Jpeg); 4 //輸出圖片流 5 bb = stream.ToArray();
這句的重要性也不可忽視,主要就把畫的內容保存到流中方便使用
7. 最後千萬不用忘了使用Dispose釋放畫布
» mvc登錄操作測試驗證碼正確性
有了上面驗證碼生成類生成好的驗證碼圖片,那麼我們還需要測試驗證下正確性和效果;下面我們使用mvc架構來做測試,先創建一個驗證碼測試Action並生成對應試圖ValidCode.cshtml文件,然後自定義幾個不同格式的驗證碼獲取Action,代碼如下:
1 public FileResult GetValidateCode()
2 {
3 //返回的驗證碼文字
4 var code = string.Empty;
5 var bb_code = ValidateCode.GetValidateCodeStream(ref code);
6
7 return File(bb_code, "image/jpeg");
8 }
9 public FileResult GetValidateCode01()
10 {
11 var code = string.Empty;
12 var bb_code = ValidateCode.GetValidateCodeStream(ref code, "1|2|3|4");
13 return File(bb_code, "image/jpeg");
14 }
15 public FileResult GetValidateCode02()
16 {
17 var code = string.Empty;
18 var bb_code = ValidateCode.GetValidateCodeStream(ref code, "4|3|2|1");
19 return File(bb_code, "image/jpeg");
20 }
21 public FileResult GetValidateCode03()
22 {
23 var code = string.Empty;
24 var bb_code = ValidateCode.GetValidateCodeStream(ref code, "2|2|2|2");
25 return File(bb_code, "image/jpeg");
26 }
27 public FileResult GetValidateCode04()
28 {
29 var code = string.Empty;
30 var bb_code = ValidateCode.GetValidateCodeStream(ref code, "4|4|4|4");
31 return File(bb_code, "image/jpeg");
32 }
33 public FileResult GetValidateCode05()
34 {
35 var code = string.Empty;
36 var bb_code = ValidateCode.GetValidateCodeStream(ref code, "1|1|1|1");
37 return File(bb_code, "image/jpeg");
38 }
感覺上幾乎一模一樣,只是對應的參數不一樣,這裡遵循的方法GetValidateCodeStream參數codeType格式是:為空表示自由組合 1:小寫拼音 2:大寫拼音 3:數字 4:漢字;然後我們往試圖中填寫如下代碼:
1 <h2>神牛 - 驗證碼實例</h2> 2 3 <div class="container " id="appVue"> 4 <table class="table table-bordered text-left"> 5 <tbody> 6 <tr> 7 <td>全部隨機</td> 8 <td> 9 <img src="/home/GetValidateCode" data-src="/home/GetValidateCode" id="imgCode" /> 10 <input type="text" name="code" placeholder="請輸入驗證碼" class="form-control" /> 11 <button class="btn btn-default">登 錄</button> 12 <span id="msg" ></span> 13 </td> 14 </tr> 15 <tr> 16 <td>小寫|大寫|數字|漢字</td> 17 <td><img src="/home/GetValidateCode01" data-src="/home/GetValidateCode01" /></td> 18 </tr> 19 <tr> 20 <td>漢字|數字|大寫|小寫</td> 21 <td><img src="/home/GetValidateCode02" data-src="/home/GetValidateCode02" /></td> 22 </tr> 23 <tr> 24 <td>全部大寫</td> 25 <td><img src="/home/GetValidateCode03" data-src="/home/GetValidateCode03" /></td> 26 </tr> 27 <tr> 28 <td>全部漢字</td> 29 <td><img src="/home/GetValidateCode04" data-src="/home/GetValidateCode04" /></td> 30 </tr> 31 <tr> 32 <td>全部小寫</td> 33 <td><img src="/home/GetValidateCode05" data-src="/home/GetValidateCode05" /></td> 34 </tr> 35 </tbody> 36 </table> 37 </div>
好了咋們生成下項目,看下效果圖如下:

能從圖中看到我們驗證碼格式的不同之處,這也是文章開頭說的驗證碼格式的多樣性,當然可能還有其他組成格式請允許我暫時忽略,下面我們來做一個點擊圖片獲取新驗證碼的功能和點擊登錄按鈕去後台程序判斷驗證碼是否匹配的例子,先來修改試圖界面代碼如下:
1 @{
2 ViewBag.Title = "ValidtCode";
3 }
4
5 <h2>神牛 - 驗證碼實例</h2>
6
7 <div class="container " id="appVue">
8 <table class="table table-bordered text-left">
9 <tbody>
10 <tr>
11 <td>全部隨機</td>
12 <td>
13 <img src="/home/GetValidateCode" data-src="/home/GetValidateCode" id="imgCode" />
14 <input type="text" name="code" placeholder="請輸入驗證碼" class="form-control" />
15 <button class="btn btn-default">登 錄</button>
16 <span id="msg" ></span>
17 </td>
18 </tr>
19 <tr>
20 <td>小寫|大寫|數字|漢字</td>
21 <td><img src="/home/GetValidateCode01" data-src="/home/GetValidateCode01" /></td>
22 </tr>
23 <tr>
24 <td>漢字|數字|大寫|小寫</td>
25 <td><img src="/home/GetValidateCode02" data-src="/home/GetValidateCode02" /></td>
26 </tr>
27 <tr>
28 <td>全部大寫</td>
29 <td><img src="/home/GetValidateCode03" data-src="/home/GetValidateCode03" /></td>
30 </tr>
31 <tr>
32 <td>全部漢字</td>
33 <td><img src="/home/GetValidateCode04" data-src="/home/GetValidateCode04" /></td>
34 </tr>
35 <tr>
36 <td>全部小寫</td>
37 <td><img src="/home/GetValidateCode05" data-src="/home/GetValidateCode05" /></td>
38 </tr>
39 </tbody>
40 </table>
41 </div>
42 <script src="~/Scripts/jquery-1.10.2.min.js"></script>
43 <script type="text/javascript">
44
45 $(function () {
46 $("img").on("click", function () {
47 var nowTime = new Date().getTime();
48 var src = $(this).attr("data-src") + "?t=" + nowTime;
49 if (src.length <= 0) { return; }
50 $(this).attr("src", src);
51 });
52
53 $("button").on("click", function () {
54
55 var msg = $("#msg");
56 var code = $("input[name='code']").val();
57 if (code.length <= 0) { msg.html("請輸入驗證碼!"); return; }
58
59 $.post("/home/UserLogin", { code: code }, function (result) {
60 if (result) {
61 msg.html(result.Msg);
62 if (!result.IsOk) {
63 $("#imgCode").click();
64 }
65 }
66 });
67 })
68 })
69 </script>
然後在Controller中增加如下登錄驗證代碼:
1 public JsonResult UserLogin(string code)
2 {
3 var data = new Stage.Com.Extend.StageModel.MoData();
4 if (string.IsNullOrWhiteSpace(code)) { data.Msg = "驗證碼不能為空"; return Json(data); }
5
6 var compareCode = Session["code"];
7 if (!compareCode.Equals(code)) { data.Msg = "驗證碼錯誤"; return Json(data); }
8
9 data.IsOk = true;
10 data.Msg = "驗證碼驗證成功";
11 return Json(data);
12 }
13
14 public FileResult GetValidateCode()
15 {
16 //返回的驗證碼文字
17 var code = string.Empty;
18 var bb_code = ValidateCode.GetValidateCodeStream(ref code);
19
20 var key = "code";
21 if (Session[key] != null)
22 {
23 Session.Remove(key);
24 }
25 Session[key] = code;
26 return File(bb_code, "image/jpeg");
27 }
由於我這裡無法截動態圖,所點擊測試獲取驗證碼我這裡直接給出線上的一個例子,各位可以試試:http://lovexins.com:1001/home/ValidCode,點擊獲取新驗證碼的關鍵代碼是: $(this).attr("src", src); 重新給img元素的scr賦值,不過這裡要注意由於浏覽器緩存的原因,這裡賦值的時候需要加上一個動態參數,我這裡是使用時間作為請求參數,因此有了以下的代碼: $(this).attr("data-src") + "?t=" + nowTime; 這是特別的地方需要注意;好了咋們來直接測試登陸是否能從後端判斷驗證碼是否正確匹配吧,這裡用的是session來保存獲取驗證碼圖片返回的驗證代碼,然後在登陸時候判斷用戶數據的驗證碼是否和後台session的驗證一樣:
驗證失敗:

驗證成功:

好了測試用例就這麼多,如果您覺得我這個驗證碼生成例子還可以並且您希望使用那麼請注意,參數的傳遞,不同得到的驗證碼格式不同,主要方法是:
1 /// <summary>
2 /// 獲取驗證碼圖片流
3 /// </summary>
4 /// <param name="codeLen">驗證碼個數(codeType設置 > codeLen設置)</param>
5 /// <param name="codeType">為空表示自由組合 1:小寫拼音 2:大寫拼音 3:數字 4:漢字</param>
6 /// <returns></returns>
7 public static byte[] GetValidateCodeStream(ref string code, string codeType = "", int codeLen = 0, int fontSize = 18, int width = 120, int height = 30)
8 {
9 //為空自由組合
10 if (string.IsNullOrWhiteSpace(codeType))
11 {
12 for (int i = 0; i < codeLen; i++)
13 {
14 codeType += rm.Next(1, 5) + "|";
15 }
16 }
17 code = CreateCode(codeType);
18 return CreateValidateCodeStream(code, fontSize, width: width, height: height);
19 }
具體參數各位可以看下備注,我這裡順便打包下代碼,方便分享和使用:神牛-驗證碼生成示例,測試用例地址:http://lovexins.com:1001/home/ValidCode