
能看到此图描述的编码生成池对应的是几个不同的编码内容,这里主要根据参数设置允许同时获取不同编码内容,从而到达文字,拼音,汉字组合而成验证码,具体规则设置由参数而定;
» 验证码生成池代码的解析
首先,由上面流程图分析的内容能看出,这个验证码生成池子需要并行获取不同类型验证码数据,才能满足组合的验证码,因此有了下面的代码:
/// <summary> /// 创建验证码 /// </summary> /// <param name="codeType">1:小写拼音 2:大写拼音 3:数字 4:汉字</param> /// <returns></returns> public static string CreateCode(string codeType = "1|2|3|4") {var code = string.Empty;try{if (string.IsNullOrWhiteSpace(codeType) || codeType.IndexOf("|") < 0) { codeType = "1|2|3|4"; }var codeTypeArr = codeType.Split(new char[] { "|" }, StringSplitOptions.RemoveEmptyEntries);var strLen = codeTypeArr.Length;//任务Task<string>[] taskArr = new Task<string>[strLen];for (int i = 0; i < strLen; i++){ var val = codeTypeArr[i]; switch (val) { case "1": //小写拼音taskArr[i] = Task.Factory.StartNew<string>(() => { return GetPinYinOrUpper(false); });break; case "2": //大写拼音taskArr[i] = Task.Factory.StartNew<string>(() => { return GetPinYinOrUpper(); });break; case "3": //数字taskArr[i] = Task.Factory.StartNew<string>(() => { return GetShuZi(); });break; case "4": //汉字taskArr[i] = Task.Factory.StartNew<string>(() => { return GetHanZi(); });break; default:break; }}//等待完成 30sTask.WaitAll(taskArr, TimeSpan.FromSeconds(30));foreach (var item in taskArr){ code += item.Result;}}catch (Exception ex){code = "我爱祖国";}return code; }这里继续使用了关键字Task,来分发任务获取不同的验证码内容,个人认为最主要的还是通过参数设置 string codeType = "1|2|3|4" ,来确定验证码的组合方式,这样也达到了验证码格式的多样性;/// <summary> /// 生成验证码图片流 /// </summary> /// <param name="code">验证码文字</param> /// <returns>流</returns> public static byte[] CreateValidateCodeStream(string code = "我爱祖国", int fontSize = 18, int width = 0, int height = 0, string fontFamily = "华文楷体") {var bb = new byte[0];//初始化画布var padding = 2;var len = code.Length;width = width <= 0 ? fontSize * 2 * (len - 1) + padding * 4 : width;height = height <= 0 ? fontSize * 2 : height;var image = new Bitmap(width, height);var g = Graphics.FromImage(image);try{var random = new Random();//清空背景色g.Clear(Color.White);//画横向中间干扰线var x1 = 0;var y1 = height / 2;var x2 = width;var y2 = y1;g.DrawLine(new Pen(Color.DarkRed), x1, y1, x2, y2);//字体var font = new Font(fontFamily, fontSize, (FontStyle.Bold | FontStyle.Italic));var brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), Color.Blue, Color.DarkRed, 1f, true);//画文字var stringFomart = new StringFormat();//垂直居中stringFomart.LineAlignment = StringAlignment.Center;//水平居中stringFomart.Alignment = StringAlignment.Center;var rf = new Rectangle(Point.Empty, new Size(width, height));g.DrawString(code, font, brush, rf, stringFomart);//画图片的前景干扰点for (int i = 0; i < 100; i++){ var x = random.Next(image.Width); var y = random.Next(image.Height); image.SetPixel(x, y, Color.FromArgb(random.Next()));}//画图片的边框线g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);//保存图片流var stream = new MemoryStream();image.Save(stream, ImageFormat.Jpeg);//输出图片流bb = stream.ToArray();}catch (Exception ex) { }finally{g.Dispose();image.Dispose();}return bb; }这个列出画验证码图片方法的关键点:var stringFomart = new StringFormat(); //垂直居中 stringFomart.LineAlignment = StringAlignment.Center; //水平居中 stringFomart.Alignment = StringAlignment.Center;5. g.DrawString(code, font, brush, rf, stringFomart); 主要用来把文字画到图片上,这是最关键的地方
//保存图片流 var stream = new MemoryStream(); image.Save(stream, ImageFormat.Jpeg); //输出图片流 bb = stream.ToArray();这句的重要性也不可忽视,主要就把画的内容保存到流中方便使用
public FileResult GetValidateCode() {//返回的验证码文字var code = string.Empty;var bb_code = ValidateCode.GetValidateCodeStream(ref code);return File(bb_code, "image/jpeg"); } public FileResult GetValidateCode01() {var code = string.Empty;var bb_code = ValidateCode.GetValidateCodeStream(ref code, "1|2|3|4");return File(bb_code, "image/jpeg"); } public FileResult GetValidateCode02() {var code = string.Empty;var bb_code = ValidateCode.GetValidateCodeStream(ref code, "4|3|2|1");return File(bb_code, "image/jpeg"); } public FileResult GetValidateCode03() {var code = string.Empty;var bb_code = ValidateCode.GetValidateCodeStream(ref code, "2|2|2|2");return File(bb_code, "image/jpeg"); } public FileResult GetValidateCode04() {var code = string.Empty;var bb_code = ValidateCode.GetValidateCodeStream(ref code, "4|4|4|4");return File(bb_code, "image/jpeg"); } public FileResult GetValidateCode05() {var code = string.Empty;var bb_code = ValidateCode.GetValidateCodeStream(ref code, "1|1|1|1");return File(bb_code, "image/jpeg"); }感觉上几乎一模一样,只是对应的参数不一样,这里遵循的方法GetValidateCodeStream参数codeType格式是:为空表示自由组合 1:小写拼音 2:大写拼音 3:数字 4:汉字;然后我们往试图中填写如下代码:<h2>神牛 - 验证码实例</h2><div class="container " id="appVue"> <table class="table table-bordered text-left"> <tbody><tr><td>全部随机</td><td> <img src="/home/GetValidateCode" data-src="/home/GetValidateCode" id="imgCode" /> <input type="text" name="code" placeholder="请输入验证码" class="form-control" /> <button class="btn btn-default">登 录</button> <span id="msg" style="color:red"></span></td></tr><tr><td>小写|大写|数字|汉字</td><td><img src="/home/GetValidateCode01" data-src="/home/GetValidateCode01" /></td></tr><tr><td>汉字|数字|大写|小写</td><td><img src="/home/GetValidateCode02" data-src="/home/GetValidateCode02" /></td></tr><tr><td>全部大写</td><td><img src="/home/GetValidateCode03" data-src="/home/GetValidateCode03" /></td></tr><tr><td>全部汉字</td><td><img src="/home/GetValidateCode04" data-src="/home/GetValidateCode04" /></td></tr><tr><td>全部小写</td><td><img src="/home/GetValidateCode05" data-src="/home/GetValidateCode05" /></td></tr> </tbody> </table></div>好了咋们生成下项目,看下效果图如下:

能从图中看到我们验证码格式的不同之处,这也是文章开头说的验证码格式的多样性,当然可能还有其他组成格式请允许我暂时忽略,下面我们来做一个点击图片获取新验证码的功能和点击登录按钮去后台程序判断验证码是否匹配的例子,先来修改试图界面代码如下:
@{ ViewBag.Title = "ValidtCode";}<h2>神牛 - 验证码实例</h2><div class="container " id="appVue"> <table class="table table-bordered text-left"> <tbody><tr><td>全部随机</td><td> <img src="/home/GetValidateCode" data-src="/home/GetValidateCode" id="imgCode" /> <input type="text" name="code" placeholder="请输入验证码" class="form-control" /> <button class="btn btn-default">登 录</button> <span id="msg" style="color:red"></span></td></tr><tr><td>小写|大写|数字|汉字</td><td><img src="/home/GetValidateCode01" data-src="/home/GetValidateCode01" /></td></tr><tr><td>汉字|数字|大写|小写</td><td><img src="/home/GetValidateCode02" data-src="/home/GetValidateCode02" /></td></tr><tr><td>全部大写</td><td><img src="/home/GetValidateCode03" data-src="/home/GetValidateCode03" /></td></tr><tr><td>全部汉字</td><td><img src="/home/GetValidateCode04" data-src="/home/GetValidateCode04" /></td></tr><tr><td>全部小写</td><td><img src="/home/GetValidateCode05" data-src="/home/GetValidateCode05" /></td></tr> </tbody> </table></div><script src="~/Scripts/jquery-1.10.2.min.js"></script><script type="text/javascript"> $(function () { $("img").on("click", function () {var nowTime = new Date().getTime();var src = $(this).attr("data-src") + "?t=" + nowTime;if (src.length <= 0) { return; }$(this).attr("src", src); }); $("button").on("click", function () {var msg = $("#msg");var code = $("input[name="code"]").val();if (code.length <= 0) { msg.html("请输入验证码!"); return; }$.post("/home/UserLogin", { code: code }, function (result) {if (result) { msg.html(result.Msg); if (!result.IsOk) { $("#imgCode").click(); }}}); }) })</script>然后在Controller中增加如下登录验证代码:public JsonResult UserLogin(string code) {var data = new Stage.Com.Extend.StageModel.MoData();if (string.IsNullOrWhiteSpace(code)) { data.Msg = "验证码不能为空"; return Json(data); }var compareCode = Session["code"];if (!compareCode.Equals(code)) { data.Msg = "验证码错误"; return Json(data); }data.IsOk = true;data.Msg = "验证码验证成功";return Json(data); } public FileResult GetValidateCode() {//返回的验证码文字var code = string.Empty;var bb_code = ValidateCode.GetValidateCodeStream(ref code);var key = "code";if (Session[key] != null){Session.Remove(key);}Session[key] = code;return File(bb_code, "image/jpeg"); }由于我这里无法截动态图,所点击测试获取验证码我这里直接给出线上的一个例子,各位可以试试:http://lovexins.com:1001/home/ValidCode,点击获取新验证码的关键代码是: $(this).attr("src", src); 重新给img元素的scr赋值,不过这里要注意由于浏览器缓存的原因,这里赋值的时候需要加上一个动态参数,我这里是使用时间作为请求参数,因此有了以下的代码: $(this).attr("data-src") + "?t=" + nowTime; 这是特别的地方需要注意;好了咋们来直接测试登陆是否能从后端判断验证码是否正确匹配吧,这里用的是session来保存获取验证码图片返回的验证代码,然后在登陆时候判断用户数据的验证码是否和后台session的验证一样:
验证成功:

好了测试用例就这么多,如果您觉得我这个验证码生成例子还可以并且您希望使用那么请注意,参数的传递,不同得到的验证码格式不同,主要方法是:
/// <summary> /// 获取验证码图片流 /// </summary> /// <param name="codeLen">验证码个数(codeType设置 > codeLen设置)</param> /// <param name="codeType">为空表示自由组合 1:小写拼音 2:大写拼音 3:数字 4:汉字</param> /// <returns></returns> public static byte[] GetValidateCodeStream(ref string code, string codeType = "", int codeLen = 0, int fontSize = 18, int width = 120, int height = 30) {//为空自由组合if (string.IsNullOrWhiteSpace(codeType)){for (int i = 0; i < codeLen; i++){ codeType += rm.Next(1, 5) + "|";}}code = CreateCode(codeType);return CreateValidateCodeStream(code, fontSize, width: width, height: height); }具体参数各位可以看下备注,我这里顺便打包下代码,方便分享和使用:验证码生成示例