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 }
三.总结
这个是我们暂时想到的图片验证码实现方案。不知道其他博友公司是如何实现的。希望大家也可以分享一下。


浙公网安备 33010602011771号