背景: 因为移动端APP和Msite手机注册发送短信验证码没有添加图片验证码功能。公司的短信接口被恶意刷取。所以我们就觉得在移动端添加一个图片验证码功能。分享一下大体实现方式思路。PS demo是自己写的。跟公司代码还是有很大差距的。

   一. 图片验证码第一版   

      1. 建立图片验证码 ValidationCodeHelper

          1.1 填写方法生成对应的.验证码: 默认是4位数字

 1         private static char[] _constant = {  
 2         '0','1','2','3','4','5','6','7','8','9',  
 3         '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',   
 4         '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'   
 5          };
 6 
 7         public static string CreateValidateCode(int length = 4, bool isNum = true)
 8         {
 9             var sb = new StringBuilder();
10             var constant = isNum ? _constant.Take(10).ToArray() : _constant;
11             var constant_count = constant.Count();
12             Random rd = new Random();
13             for (var index = 0; index < length; index++)
14             {
15                 sb.Append(constant[rd.Next(constant_count)]);
16             }
17             return sb.ToString();
18         }

            1.2 通过验证码生成图片流, 此代码是从其他博友那里Copy过来的。自己对图片方面不擅长

 1 public static byte[] GetImage(string code)
 2         {
 3             Bitmap image = new Bitmap((int)Math.Ceiling(code.Length * 16.0), 27);
 4             Graphics g = Graphics.FromImage(image);
 5             try
 6             {
 7                 Random random = new Random();
 8                 g.Clear(Color.Gray);
 9                 for (int i = 0; i < 25; i++)
10                 {
11                     int x1 = random.Next(image.Width);
12                     int x2 = random.Next(image.Width);
13                     int y1 = random.Next(image.Height);
14                     int y2 = random.Next(image.Height);
15                     g.DrawLine(new Pen(Color.Silver), x1, x2, y1, y2);
16                 }
17                 Font font = new Font("Arial", 13, (FontStyle.Bold | FontStyle.Italic));
18                 LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), Color.Blue, Color.DarkRed, 1.2f, true);
19                 g.DrawString(code, font, brush, 3, 2);
20                 for (int i = 0; i < 100; i++)
21                 {
22                     int x = random.Next(image.Width);
23                     int y = random.Next(image.Height);
24                     image.SetPixel(x, y, Color.FromArgb(random.Next()));
25                 }
26                 g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
27                 MemoryStream stream = new MemoryStream();
28                 image.Save(stream, ImageFormat.Jpeg);
29                 return stream.ToArray();
30             }
31             catch (Exception ex)
32             {
33                 return null;
34             }
35             finally
36             {
37                 g.Dispose();
38                 image.Dispose();
39             }
40         }

        2. 封装一个简单的CookieHelper类型主要是对Cookie进行加密。只是简单封装, 没有对CookieHelper添加泛型,未支持Object处理。公司里面的CookieHelper库更强大。可是不能分享代码出来。所以自己简单的写了一个。

 1     public class CookieHelper
 2     {
 3 
 4         #region 字符串加密解密
 5 
 6         private static string _MD5 = "8ff0c65d-a2ed-4e1e-af85-690c08b8d039";
 7 
 8         private static string Decrypt(string cipherString)
 9         {
10             byte[] keyArray;
11             byte[] toEncryptArray = Convert.FromBase64String(cipherString);
12             MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
13             keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(_MD5));
14             hashmd5.Clear();
15 
16             TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
17             tdes.Key = keyArray;
18             tdes.Mode = CipherMode.ECB;
19             tdes.Padding = PaddingMode.PKCS7;
20             ICryptoTransform cTransform = tdes.CreateDecryptor();
21             byte[] resultArray = cTransform.TransformFinalBlock(
22                                  toEncryptArray, 0, toEncryptArray.Length);              
23             tdes.Clear();
24             return UTF8Encoding.UTF8.GetString(resultArray);
25         }
26 
27         private static string Encrypt(string toEncrypt)
28         {
29             byte[] keyArray;
30             byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
31             MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
32             keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(_MD5));
33             hashmd5.Clear();
34 
35             TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
36             tdes.Key = keyArray;
37             tdes.Mode = CipherMode.ECB;
38             tdes.Padding = PaddingMode.PKCS7;
39 
40             ICryptoTransform cTransform = tdes.CreateEncryptor();
41             byte[] resultArray =
42               cTransform.TransformFinalBlock(toEncryptArray, 0,
43               toEncryptArray.Length);
44             tdes.Clear();
45             return Convert.ToBase64String(resultArray, 0, resultArray.Length);
46         }
47 
48 
49         #endregion
50 
51 
52         #region Cookie
53 
54         public static void SaveCookie(string name, string value, int expiredSecond = 0)
55         {
56             var encryptStr = Encrypt(value);
57             var collection = HttpContext.Current.Response.Cookies;
58             collection.Add(new HttpCookie(name)
59             {
60                 Value = encryptStr,
61                 Expires = expiredSecond > 0 ? System.DateTime.Now.AddSeconds(expiredSecond) : System.DateTime.MaxValue
62             });
63         }
64 
65         public static string GetCookie(string name)
66         {
67             var cookie = HttpContext.Current.Request.Cookies.Get(name);
68             if (cookie == null)
69             {
70                 return null;
71             }
72             else
73             {
74                 return Decrypt(cookie.Value);
75             }
76         }
77 
78         #endregion
79     }

       3. Controller新增图片服务。设置图片的Cookie有效期是一分钟

 1 public class HomeController : Controller
 2     {
 3         public ActionResult Index()
 4         {
 5             return View();
 6         }
 7 
 8         public FileContentResult ImageValidator()
 9         {
10             var code = ValidationCodeHelper.CreateValidateCode();
11             CookieHelper.SaveCookie("PicCode", code, 60);
12             var picByte = ValidationCodeHelper.GetImage(code);
13             return File(picByte, "image/jpeg "); 
14         }
15 
16         /// <summary>
17         /// 检查图片验证码是否正确
18         /// </summary>
19         /// <param name="code"></param>
20         /// <returns></returns>
21         public ActionResult CheckPicCode(string code)
22         {
23             var cookieCode = CookieHelper.GetCookie("PicCode");
24             if (string.IsNullOrWhiteSpace(cookieCode))
25             {
26                 return Json("验证码过期",JsonRequestBehavior.AllowGet);
27             }
28             if (code.Trim().ToUpper() == cookieCode.ToUpper())
29             {
30                 return Json("验证码正确",JsonRequestBehavior.AllowGet);
31             }
32             else
33             {
34                 return Json("验证码错误",JsonRequestBehavior.AllowGet);
35             }
36         }
37     }

         4.前台页面. 通过修改img的src链接,来实现点击刷新图片.

 1 <head>
 2     <title></title>
 3     <script src="https://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
 4 </head>
 5 <body>
 6     <input type="text" id="inpCode" />
 7     <img id="picCode" src="/Home/ImageValidator" />
 8     <button id="btnCheck" >校验</button>
 9 </body>
10 </html>
11 <script>
12 
13     $(function () {
14 
15         $("#picCode").on('click', function () {
16             $(this).attr('src', "/Home/ImageValidator?v=" + new Date().getTime());
17         })
18 
19         $("#btnCheck").on('click', function () {
20             $.ajax({
21                 type: 'get',
22                 url: "/Home/CheckPicCode",
23                 data: {
24                     code:$("#inpCode").val()
25                 },
26                 success: function (data) {
27                     alert(data);
28                 }
29             })
30         })
31 
32     })
33 
34 </script>

          5. 第一版基本代码实现了,点击图片刷新验证码,图片验证码有效期是一分钟.

  一分钟内

  

 

一分钟后,验证码过期了

 

              6.大家看上面两张图以为验证码OK了。的确。正常网站使用是OK的。因为我们设置了验证码的有效时间是一分钟。那我们看看Fiddler里面的效果。

       当我面在Fiddler里面模拟的时候Cookie的过期时间是没有的。也就是如果客户端用户抓取了PicCode的Cookie后。自己构建请求的话。Cookie是不会失效的。

        

 

     

    二. 图片验证码改版。

         从上面看来 直接设置Cookie的过期时间是不行的。那我们只能在Cookie存储Value上修改了。来看看我们代码实现方式。我们将过期时间的ticks和验证码字符串同时加密存储在了Cookie中。

13 
14         /// <summary>
15         /// 构建图片Cookie字符串,code和过期见时间用逗号隔开
16         /// </summary>
17         public static string BuildValidateCodeStorage(string code, int expiredSeconds = 60)
18         {
19             return string.Format("{0},{1}", code, System.DateTime.Now.AddSeconds(expiredSeconds).Ticks);
20         }
21 
22         /// <summary>
23         /// 解析图片验证码Cookie
24         /// </summary>
25         public static string AnysisValidateCode(string codeStorage)
26         {
27             if (string.IsNullOrWhiteSpace(codeStorage))
28             {
29                 return null;
30             }
31             else
32             {
33                 var vals = codeStorage.Split(',');
34                 if (vals.Count() != 2)
35                 {
36                     return null;
37                 }
38                 else
39                 {
40                     long ticks = 0;
41                     long.TryParse(vals[1], out ticks);
42                     if (ticks > System.DateTime.Now.Ticks)
43                     {
44                         return vals[0];
45                     }
46                     else
47                     {
48                         return null;
49                     }
50                 }
51             }
52         }

      三.总结

    这个是我们暂时想到的图片验证码实现方案。不知道其他博友公司是如何实现的。希望大家也可以分享一下。

      https://files.cnblogs.com/files/FourLeafCloverZc/ImageValidation.zip

                 

posted on 2017-03-07 17:28  FrankZC  阅读(940)  评论(0编辑  收藏  举报