C# 滑动验证码|拼图验证|SlideCaptcha


使用背景:

  关于滑动验证码的使用场所还是非常多的,如: 调取短信接口之前,和 注册请求之前 或者 频繁会调用的接口 都需要加这个拼图验证。这里先上一下效果图吧(心中无码,自然高清)。

  话不多说,开撸!

  C# 滑动验证码|拼图验证|SlideCaptcha

C# 滑动验证码|拼图验证|SlideCaptcha

 

 

实现分析:

  滑动验证码的逻辑也很简单。大概说一下:

  1,服务器生成主图+附图(从主图裁剪下来的不需要管y坐标)并且存储X坐标;

  2,前端传入本地X坐标到服务器。

  3,服务器进行计算存储X坐标和本地X坐标相差值;

  4,验证相差值是否在 0-2 之间,判断 true | false

 

    

 

后端代码:

      准备:

 

        增加SlideCaptcha文件夹,并且增加Captcha.cs   CaptchaModel.cs   Config.cs   ImgFormat.cs   4个文件。分别是:验证,验证实体,配置和图片生成类。代码如下:

 

Captcha.cs       

using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; using System.Net;   namespace SliderCaptcha.NET {     public class Captcha     {          public static Captcha64Model GenerateBase64()         {             CaptchaModel model = Captcha.Generate();             if (model != null)             {                 return new Captcha64Model()                 {                     X = model.X,                     Y = model.Y,                     Background = ImageToBase64(model.Background,ImageFormat.Jpeg),                     Slide = ImageToBase64(model.Slide, ImageFormat.Png)                 };             }             else             {                 return null;             }         }          /// <summary>         /// 生成验证码         /// </summary>         /// <returns></returns>         public static CaptchaModel Generate()         {             Bitmap image = BgImage();             if (image != null)             {                 int l = Config.l;                 int d = Config.d;                 int width = image.Width;                 int height = image.Height;                 int x = RandomNext(width / 3, width - d - l - 10);//初始x                 int y = RandomNext(10 + d, height - l - 10); ;//初始y                 GraphicsPath path = GetSliderPath(x, y);                 Graphics g = GetGraphics(image);                  //水印                 if (Config.showWatermark)                 {                     Font font = new Font("宋体", 12, FontStyle.Bold);                     SizeF size = g.MeasureString(Config.watermarkText, font);                     Point Plogo = new Point((int)(width - size.Width - 5), (int)(height - size.Height - 5));                     Color color = image.GetPixel(Plogo.X, Plogo.Y);                     SolidBrush bru = new SolidBrush(AntiColor(color));                     g.DrawString(Config.watermarkText, font, bru, Plogo);                 }                  Pen pen = new Pen(Color.FromArgb(200, 255, 255, 255), 2);                 g.DrawPath(pen, path);                 Image slider = CaptureSlider(image, path, x, width, height);                 SolidBrush brush = new SolidBrush(Color.FromArgb(100, 255, 255, 255));                 g.FillPath(brush, path);                 g.Save();                 g.Dispose();                 return new CaptchaModel()                 {                     X = x,                     Y = y,                     Background = image,                     Slide = slider                 };             }             return null;         }             /// <summary>         /// 获取图片Graphics         /// </summary>         /// <param name="image"></param>         /// <returns></returns>         private static Graphics GetGraphics(Image image)         {             Graphics g = Graphics.FromImage(image);             g.SmoothingMode = SmoothingMode.HighQuality;             g.CompositingQuality = CompositingQuality.HighQuality;             g.InterpolationMode = InterpolationMode.High;             return g;         }          /// <summary>         /// 获取滑块path         /// </summary>         private static GraphicsPath GetSliderPath(int x, int y)         {             int l = Config.l;             int r = Config.r;             int b = Config.b;             int c = Config.c;             int d = Config.d;             int blod = Config.blod;             GraphicsPath path = new GraphicsPath(FillMode.Winding);             Point Pa = new Point(x, y);             Point Pb = new Point(x + l / 2 - b, y - c + blod);             Point Pd = new Point(x + l, y);             Point Pe = new Point(Pd.X + c - blod, y + l / 2 - b);             Point Pg = new Point(Pd.X, y + l);             Point Ph = new Point(x, y + l);             Point Pj = new Point(x + c - blod, Pe.Y);             path.AddLine(Pa, Pb);             path.AddArc(x + l / 2 - r, y - d, d, d, 130f, 280f);             path.AddLines(new Point[] { Pd, Pe });             path.AddArc(x + l, y + l / 2 - r, d, d, 220f, 280f);             path.AddLines(new Point[] { Pg, Ph });             path.AddArc(x, y + l / 2 - r, d, d, 140f, -280f);             path.AddLine(Pj, Pa);             return path;         }           /// <summary>         /// 获取滑块区域         /// </summary>         /// <param name="image"></param>         /// <param name="path"></param>         /// <param name="x"></param>         /// <param name="width"></param>         /// <param name="height"></param>         /// <returns></returns>         private static Image CaptureSlider(Image image, GraphicsPath path, int x, int width, int height)         {             Bitmap concave = new Bitmap(image.Width, image.Height);             Graphics g = GetGraphics(concave);             TextureBrush brush = new TextureBrush(image);             g.Clear(Color.Transparent);             g.FillPath(brush, path);             g.Dispose();             return CaptureImage(concave, x, height);         }           /// <summary>         /// 裁剪图片         /// </summary>         /// <param name="fromImage"></param>         /// <param name="offsetX"></param>         /// <param name="width"></param>         /// <param name="height"></param>         /// <returns></returns>         private static Image CaptureImage(Image fromImage, int offsetX, int height)         {             int width = Config.l + Config.d + Config.blod;             Bitmap bitmap = new Bitmap(width, height);             Graphics g = GetGraphics(bitmap);             g.DrawImage(fromImage, 0, 0, new Rectangle(offsetX, 0, width, height), GraphicsUnit.Pixel);             g.Dispose();             return bitmap;         }           /// <summary>         /// 生成随机数         /// </summary>         /// <param name="min"></param>         /// <param name="max"></param>         /// <returns></returns>         private static int RandomNext(int min, int max)         {             Random random = new Random(Guid.NewGuid().GetHashCode());             return random.Next(min, max);         }           /// <summary>         /// 取反色         /// </summary>         /// <param name="color"></param>         /// <returns></returns>         public static Color AntiColor(Color color)         {             if (color.R > 128 && color.G > 128 && color.B > 128)             {                 return Color.Black;             }             else             {                 return Color.White;             }          }          /// <summary>         /// 获取背景图         /// </summary>         /// <returns></returns>         private static Bitmap BgImage()         {             WebClient web = new WebClient();             int num = RandomNext(1, 20);             Stream stream = web.OpenRead($"http://00x1.com/images/Pic/{num}.jpg");             Bitmap bitmap = (Bitmap)Image.FromStream(stream);             return bitmap;         }            /// <summary>         /// base64转图片         /// </summary>         /// <param name="base64string"></param>         /// <returns></returns>         public static Bitmap Base64ToImage(string base64string)         {             byte[] b = Convert.FromBase64String(base64string);             MemoryStream ms = new MemoryStream(b);             Bitmap bitmap = new Bitmap(ms);             return bitmap;         }          /// <summary>         /// 图片转base64         /// </summary>         /// <param name="image"></param>         /// <returns></returns>         public static string ImageToBase64(Image image, ImageFormat format)         {             if (image == null) return string.Empty;             string strbaser64 = "";             try             {                 string head = "";                 string formatName = ImgFormat.NameFromGuid(format);                 head = $"data:image/{formatName.ToLower()};base64,";                 MemoryStream ms = new MemoryStream();                 image.Save(ms, format);                 byte[] arr = new byte[ms.Length];                 ms.Position = 0;                 ms.Read(arr, 0, (int)ms.Length);                 ms.Close();                 strbaser64 = head+Convert.ToBase64String(arr);             }             catch (Exception)             {                 throw new Exception("Something wrong during convert!");             }             return strbaser64;         }              }  }

 

CaptchaModel.cs     

using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text;  namespace SliderCaptcha.NET {     public class CaptchaModel     {         public int X { get; set; }         public int Y { get; set; }         public Image Background { get; set; }         public Image Slide { get; set; }     }     public class Captcha64Model     {         public int X { get; set; }         public int Y { get; set; }         public string Background { get; set; }         public string Slide { get; set; }     } }

      

Config.cs
using System;  namespace SliderCaptcha.NET {     public class Config     {         /// <summary>         /// 矩形宽         /// </summary>         public static int l = 42;         /// <summary>         /// 圆形半径         /// </summary>         public static int r = 9;         /// <summary>         /// 圆形直径         /// </summary>         public static int d = r * 2;         /// <summary>         /// 计算圆形与矩形交接三角形边         /// </summary>         public static int a = (int)(r * Math.Sin(Math.PI * (50 / 180f)));         public static int b = (int)(r * Math.Cos(Math.PI * (50 / 180f)));         public static int c = r - a;         /// <summary>         /// 滑块边框         /// </summary>         public static int blod = 2;         /// <summary>         /// 水印         /// </summary>         public static string watermarkText = "SC.NET";         /// <summary>         /// 是否显示水印         /// </summary>         public static bool showWatermark = true;      } }

    

 

ImgFormat.cs
using System; using System.Collections.Generic; using System.Drawing.Imaging;  namespace SliderCaptcha.NET {     public class ImgFormat     {          private static Dictionary<string, string> Formats = new Dictionary<string, string>() {                             {"b96b3caa-0728-11d3-9d7b-0000f81ef32e","MemoryBmp"},                             {"b96b3cab-0728-11d3-9d7b-0000f81ef32e","Bmp"},                             {"b96b3cac-0728-11d3-9d7b-0000f81ef32e","Emf"},                             {"b96b3cad-0728-11d3-9d7b-0000f81ef32e","Wmf"},                             {"b96b3cae-0728-11d3-9d7b-0000f81ef32e","Jpeg"},                             {"b96b3caf-0728-11d3-9d7b-0000f81ef32e","Png"},                             {"b96b3cb0-0728-11d3-9d7b-0000f81ef32e","Gif"},                             {"b96b3cb1-0728-11d3-9d7b-0000f81ef32e","Tiff"},                             {"b96b3cb2-0728-11d3-9d7b-0000f81ef32e","Exif"},                             {"b96b3cb5-0728-11d3-9d7b-0000f81ef32e","Icon"}         };          public static ImageFormat FormatFromGuid(ImageFormat format)         {             return FormatFromGuid(format.Guid);         }         public static ImageFormat FormatFromGuid(Guid guid)         {             return FormatFromGuid(guid.ToString());         }         public static ImageFormat FormatFromGuid(string guid)         {             if (Formats.ContainsKey(guid))             {                 string name = Formats[guid];                 ImageFormat format = null;                 switch (name)                 {                     case "MemoryBmp":                         format = ImageFormat.MemoryBmp;                         break;                     case "Bmp":                         format = ImageFormat.Bmp;                         break;                     case "Emf":                         format = ImageFormat.Emf;                         break;                     case "Wmf":                         format = ImageFormat.Wmf;                         break;                     case "Gif":                         format = ImageFormat.Gif;                         break;                     case "Jpeg":                         format = ImageFormat.Jpeg;                         break;                     case "Png":                         format = ImageFormat.Png;                         break;                     case "Tiff":                         format = ImageFormat.Tiff;                         break;                     case "Exif":                         format = ImageFormat.Exif;                         break;                     case "Icon":                         format = ImageFormat.Icon;                         break;                 }                 return format;             }             else             {                 return null;             }                      }            public static string NameFromGuid(ImageFormat format)         {             return NameFromGuid(format.Guid);         }         public static string NameFromGuid(Guid guid)         {             return NameFromGuid(guid.ToString());         }         public static string NameFromGuid(string guid)         {             if (Formats.ContainsKey(guid))             {                 return Formats[guid];             }             else             {                 return string.Empty;             }          }      } }

 

        

      使用:

        这里用的是asp.net mvc 的框架,用的是api接口,前后端分离供前端使用。具体使用根据个人需求,可以是接口调用亦可以是项目调用。

        呼声如果很高的话,考虑出一个winfrom版本的滑动验证码~(滑稽)

        [HttpGet]         public IHttpActionResult GetCaptcha()         {             Captcha64Model model = Captcha.GenerateBase64();             CacheHelper.Cache.SetCache("sliderX", model.X);             Hashtable ht = new Hashtable();             ht.Add("background", model.Background);             ht.Add("slider", model.Slide);             ht.Add("sliderXXXXX", model.X);             return Json(ht);         }          /// <summary>         /// 检查验证         /// </summary>         /// <param name="x"></param>         /// <returns></returns>         [HttpPost]         public IHttpActionResult CheckCaptcha([FromBody] int x = 0)         {             Hashtable hs = new Hashtable();             string Mess = "";             int Code = 0;             var session = CacheHelper.Cache.GetCache("sliderX");             if (session == null)             {                 Mess = "请刷新验证码";                 Code = 500;                 goto block;             }             string sliderXStr = session?.ToString();// as string             int sliderX = Convert.ToInt32(sliderXStr);             int difX = sliderX - x;             if (difX >= 0 - Config.blod && difX <= Config.blod)             {                 Mess = "success";                 Code = 0;             }             else             {                 session = null;                 Mess = "错误";                 Code = 500;             }         block:             hs.Add("Mess", Mess);             hs.Add("Code", Code);             return Json(hs);         }

 

        

前端代码:

        

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head>     <title>Splide Captcha</title>     <style>         * {             -webkit-user-select: none;             -moz-user-select: none;             -ms-user-select: none;             user-select: none;         }          .sc-captcha {             width: 300px;             margin: 100px auto;         }          .sc_net_panel {             padding: 10px;         }              .sc_net_panel > div {                 position: relative;             }          .bg_slider {             position: absolute;             left: 0;             top: 0;         }          .bg_refresh {             position: absolute;             right: 5px;             top: 5px;             background: #808080;             color: #fff;             border-radius: 3px;             width: 16px;             line-height: 16px;             text-align: center;             cursor: pointer;         }          .sc_net_slider_icon {             position: absolute;             left: 0;             top: 0;             height: 37px;             text-align: center;             border-radius: 5px;             border: #808080 1px solid;             width: 37px;             line-height: 37px;             cursor: pointer;         }              .sc_net_slider_icon:hover {                 color: #fff;                 background: #1991fa;             }          .sc_net_slider_text {             position: absolute;             left: 0;             top: 0;             text-align: center;             width: 280px;             color: #45494c;             border: #808080 1px solid;             border-radius: 5px;             line-height: 35px;             height: 37px;             cursor: default;         }          .sc_net_slider_area {             position: absolute;             left: 0;             top: 0;             height: 37px;         }     </style>     <script src="E:porject碚煜ApiByzkApiScriptsjquery-3.4.1.js"></script>     <script src="E:porject碚煜ApiByzkApiScriptsjquery-3.4.1.min.js"></script>  </head> <body>     <div>         <div class="sc-captcha">             <div class="sc_net_panel">                 <div class="sc_net_bg">                     <div class="sc_net_bgimg">                     </div>                     <div class="bg_refresh" onclick="loadCaptcha()" title="刷新">↻</div>                 </div>             </div>             <div class="sc_net_panel">                 <div class="sc_net_slider">                     <div class="sc_net_slider_text">向右拖动滑块填充拼图</div>                     <div class="sc_net_slider_area"></div>                     <div class="sc_net_slider_icon">➞</div>                 </div>             </div>         </div>     </div>  </body>  <script>     loadCaptcha();     function loadCaptcha() {         $.ajax({             url: "http://192.168.1.216:5500/api/TestApi/GetCaptcha",             type: "Get",             dataType: "JSON",             success: function (json) {                 var bg = createCanvas(280, 155);                 bg.className = 'bg_img';                 bg_slider = createCanvas(62, 155);                 bg_slider.className = 'bg_slider';                 CanvasSetImage(bg, json.background);                 CanvasSetImage(bg_slider, json.slider);                 var doc = document.getElementsByClassName("sc_net_bgimg")[0];                 doc.innerHTML = "";                 doc.appendChild(bg);                 doc.appendChild(bg_slider);                  console.log(json.modelX);             }         })     }      function createCanvas(width, height) {         var canvas = document.createElement('canvas');         canvas.width = width;         canvas.height = height;         return canvas;     };       function CanvasSetImage(_canvas, base64) {          //获取2d画布对象         var ctx = _canvas.getContext("2d");         //创建图片标签         var _img = document.createElement("img");         //设置图片地址         _img.src = base64;          //ctx.fillRect(0, 0, _canvas.clientWidth, _canvas.clientHeight);         //ctx.fillStyle = 'rgba(255, 255, 255, 0)';         _img.onload = function () {             ctx.drawImage(_img, 0, 0);         }         /*           ctx.drawImage(_img, 10, 10);*/     }      function getByClassName0(className) {         return document.getElementsByClassName(className)[0];     };      var slider = getByClassName0("sc_net_slider_icon");     var text = getByClassName0("sc_net_slider_text");     var area = getByClassName0("sc_net_slider_area");     var bg_slider;      slider.addEventListener('mousedown', handleDragStart);     slider.addEventListener('touchstart', handleDragStart);     document.addEventListener('mousemove', handleDragMove);     document.addEventListener('touchmove', handleDragMove);     document.addEventListener('mouseup', handleDragEnd);     document.addEventListener('touchend', handleDragEnd);     document.addEventListener('mousedown', function () { return false; });     document.addEventListener('touchstart', function () { return false; });      var originX, originY, trail = [],         isMouseDown = false;      var isOk = false;      function handleDragStart(e) {         console.log("handleDragStart");         if (isOk) return;         originX = e.clientX || e.touches[0].clientX;         originY = e.clientY || e.touches[0].clientY;         isMouseDown = true;     };      function handleDragMove(e) {         if (!isMouseDown) return false;         var eventX = e.clientX || e.touches[0].clientX;         var eventY = e.clientY || e.touches[0].clientY;         var moveX = eventX - originX;         var moveY = eventY - originY;         if (moveX >= 0 && moveX <= 243) {             slider.style.left = moveX + "px";             bg_slider.style.left = moveX / 243 * 218 + "px";         }      };      function handleDragEnd(e) {         if (!isMouseDown)             return false         isMouseDown = false         var eventX = e.clientX || e.changedTouches[0].clientX         if (eventX == originX)             return false          //获取前端的x坐标;         var a = $(".bg_slider");         var localX = a[0].offsetLeft;           $.ajax({             url: "http://192.168.1.216:5500/api/TestApi/CheckCaptcha?x=" + localX,             type: "Post",             dataType: "JSON",             success: function (res) {                 alert(res.Mess);             }         })     }; </script> </html>

结语:

    到这里基本上就已经完成了,以上前端地址更换成自己的接口即可。正常业务 验证码是很频发的一个操作并且是多用户。所以缓存存储的时候也需要注意一下。最后有问题的话,可以评论区沟通。感谢观看!(这次很用心在写,能否上个推荐~)

 

发表评论

相关文章