• WebSite---前台系统图片验证码心得


            背景: 因为移动端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         }

          三.总结

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

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

                     

  • 相关阅读:
    Junit单元测试
    点餐系统
    文件的横纵转换
    零碎知识--日积月累
    json校验
    程序员必须收藏的14个顶级开发社区!
    管理员权限
    Thinking In Java 读书笔记
    学生考试系统
    JeeSite开发笔记
  • 原文地址:https://www.cnblogs.com/FourLeafCloverZc/p/6514418.html
Copyright © 2020-2023  润新知