本次和大家分享的是一个集成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" style="color:red"></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" style="color:red"></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