图形验证码+短信验证码实战

  • 图形验证码+短信验证码实战已关闭评论
  • 118 次浏览
  • A+
所属分类:.NET技术
摘要

  上一篇分分享了基于阿里云实现的短信验证码文章,考虑到为了防止登录时,非人工操作,频繁获取验证码,趁热打铁,现在添加了图片验证码服务功能。借鉴网上传统的做法,把实现这两个验证的功能做成有个独立的服务,通过Http分别请求获取校验图片验证码和短信验证码。


前言:

  上一篇分分享了基于阿里云实现的短信验证码文章,考虑到为了防止登录时,非人工操作,频繁获取验证码,趁热打铁,现在添加了图片验证码服务功能。借鉴网上传统的做法,把实现这两个验证的功能做成有个独立的服务,通过Http分别请求获取校验图片验证码和短信验证码。

一、需求描述:

  • 图形验证码为,短信验证码为6位纯数字
  • 同一系统图片验证码缓存中只存在一个,没有有效期,每次刷新更新旧图形验证码
  • 短信验证码有效期2分钟
  • 每个手机号60秒内只能发送一次短信验证码,在服务器端执行校验
  • 同一个手机号在同一时间内可以有多个有效的短信验证码,根据不同系统类型区分
  • 每个短信验证码至多可被使用3次,无论和请求中的验证码是否匹配,随后立即作废,以防止暴力攻击
  • 发送短信验证码之前,先验证图形验证码是否正确

二、图片验证码实现:

生成随机验证码字符串:

 /// <summary>         /// 获取随机验证码         /// </summary>         /// <returns></returns>         public static string GenerateCaptchaCode()         {             Random rand = new Random();             int maxRand = Letters.Length - 1;              StringBuilder sb = new StringBuilder();              for (int i = 0; i < 4; i++)             {                 int index = rand.Next(maxRand);                 sb.Append(Letters[index]);             }              return sb.ToString();         } 

随机验证码生成验证码图片:

 /// <summary>         /// 生成随机验证码图片         /// </summary>         /// <param name="width"></param>         /// <param name="height"></param>         /// <param name="captchaCode"></param>         /// <returns></returns>         public static CaptchaResult GenerateCaptcha(int width, int height, string captchaCode)         {             using (Bitmap baseMap = new Bitmap(width, height))             using (Graphics graph = Graphics.FromImage(baseMap))             {                 Random rand = new Random();                  graph.Clear(GetRandomLightColor());                  DrawCaptchaCode();                 DrawDisorderLine();                 AdjustRippleEffect();                  MemoryStream ms = new MemoryStream();                  baseMap.Save(ms, ImageFormat.Png);                  return new CaptchaResult { CaptchaCode = captchaCode, CaptchaByteData = ms.ToArray(), Timestamp = DateTime.Now };                  int GetFontSize(int imageWidth, int captchCodeCount)                 {                     var averageSize = imageWidth / captchCodeCount;                      return Convert.ToInt32(averageSize);                 }                  Color GetRandomDeepColor()                 {                     int redlow = 160, greenLow = 100, blueLow = 160;                     return Color.FromArgb(rand.Next(redlow), rand.Next(greenLow), rand.Next(blueLow));                 }                  Color GetRandomLightColor()                 {                     int low = 180, high = 255;                      int nRend = rand.Next(high) % (high - low) + low;                     int nGreen = rand.Next(high) % (high - low) + low;                     int nBlue = rand.Next(high) % (high - low) + low;                      return Color.FromArgb(nRend, nGreen, nBlue);                 }                  void DrawCaptchaCode()                 {                     SolidBrush fontBrush = new SolidBrush(Color.Black);                     int fontSize = GetFontSize(width, captchaCode.Length);                     Font font = new Font(FontFamily.GenericSerif, fontSize, FontStyle.Bold, GraphicsUnit.Pixel);                     for (int i = 0; i < captchaCode.Length; i++)                     {                         fontBrush.Color = GetRandomDeepColor();                          int shiftPx = fontSize / 6;                          //float x = i * fontSize + rand.Next(-shiftPx, shiftPx) + rand.Next(-shiftPx, shiftPx);                         float x = i * fontSize + rand.Next(-shiftPx, shiftPx) / 2;                         //int maxY = height - fontSize;                         int maxY = height - fontSize * 2;                         if (maxY < 0)                         {                             maxY = 0;                         }                         float y = rand.Next(0, maxY);                          graph.DrawString(captchaCode[i].ToString(), font, fontBrush, x, y);                     }                 }                  void DrawDisorderLine()                 {                     Pen linePen = new Pen(new SolidBrush(Color.Black), 2);                     //for (int i = 0; i < rand.Next(3, 5); i++)                     for (int i = 0; i < 2; i++)                     {                         linePen.Color = GetRandomDeepColor();                          Point startPoint = new Point(rand.Next(0, width), rand.Next(0, height));                         Point endPoint = new Point(rand.Next(0, width), rand.Next(0, height));                         graph.DrawLine(linePen, startPoint, endPoint);                          //Point bezierPoint1 = new Point(rand.Next(0, width), rand.Next(0, height));                         //Point bezierPoint2 = new Point(rand.Next(0, width), rand.Next(0, height));                          //graph.DrawBezier(linePen, startPoint, bezierPoint1, bezierPoint2, endPoint);                     }                 }                  void AdjustRippleEffect()                 {                     short nWave = 6;                     int nWidth = baseMap.Width;                     int nHeight = baseMap.Height;                      Point[,] pt = new Point[nWidth, nHeight];                      for (int x = 0; x < nWidth; ++x)                     {                         for (int y = 0; y < nHeight; ++y)                         {                             var xo = nWave * Math.Sin(2.0 * 3.1415 * y / 128.0);                             var yo = nWave * Math.Cos(2.0 * 3.1415 * x / 128.0);                              var newX = x + xo;                             var newY = y + yo;                              if (newX > 0 && newX < nWidth)                             {                                 pt[x, y].X = (int)newX;                             }                             else                             {                                 pt[x, y].X = 0;                             }                               if (newY > 0 && newY < nHeight)                             {                                 pt[x, y].Y = (int)newY;                             }                             else                             {                                 pt[x, y].Y = 0;                             }                         }                     }                      Bitmap bSrc = (Bitmap)baseMap.Clone();                      BitmapData bitmapData = baseMap.LockBits(new Rectangle(0, 0, baseMap.Width, baseMap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);                     BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);                      int scanline = bitmapData.Stride;                      IntPtr scan0 = bitmapData.Scan0;                     IntPtr srcScan0 = bmSrc.Scan0;                      unsafe                     {                         byte* p = (byte*)(void*)scan0;                         byte* pSrc = (byte*)(void*)srcScan0;                          int nOffset = bitmapData.Stride - baseMap.Width * 3;                          for (int y = 0; y < nHeight; ++y)                         {                             for (int x = 0; x < nWidth; ++x)                             {                                 var xOffset = pt[x, y].X;                                 var yOffset = pt[x, y].Y;                                  if (yOffset >= 0 && yOffset < nHeight && xOffset >= 0 && xOffset < nWidth)                                 {                                     if (pSrc != null)                                     {                                         p[0] = pSrc[yOffset * scanline + xOffset * 3];                                         p[1] = pSrc[yOffset * scanline + xOffset * 3 + 1];                                         p[2] = pSrc[yOffset * scanline + xOffset * 3 + 2];                                     }                                 }                                  p += 3;                             }                             p += nOffset;                         }                     }                      baseMap.UnlockBits(bitmapData);                     bSrc.UnlockBits(bmSrc);                     bSrc.Dispose();                 }             }         } 

三、短信验证码实现:

见上篇:

传送门:基于Aliyun短信验证码实现

四、图片验证码获取与校验:

获取:

代码:

  /// <summary>         /// 获取图片验证码         /// </summary>         /// <param name="imgCaptchaDto">图形验证码请求信息</param>         [HttpGet("img")]         public IActionResult GetImageCaptcha([FromQuery]ImgCaptchaDto imgCaptchaDto)         {             var result = _captchaService.GetImageCaptcha(imgCaptchaDto);             var stream = new MemoryStream(result.CaptchaByteData);              return new FileStreamResult(stream, "image/png");         }  

Http调用:详细调用参数和方式见下方接口在线文档。
图形验证码+短信验证码实战

校验:

代码:

 /// <summary>         /// 验证图片验证码         /// </summary>         /// <param name="imgCaptchaDto">图形验证码信息</param>         /// <returns></returns>         [HttpPost("img")]         public IActionResult ValidateImageCaptcha(ImgCaptchaDto imgCaptchaDto)         {             bool isCaptchaValid = _captchaService.ValidateImageCaptcha(imgCaptchaDto);             if (isCaptchaValid)             {                 HttpResponseDto httpResponseDto = new HttpResponseDto()                 {                     IsSuccess = true,                     Code = StatusCodes.Status200OK,                     Message = "图形验证码验证成功"                 };                 var responJson = JsonConvert.SerializeObject(httpResponseDto);                 return Ok(responJson);             }             else             {                 HttpResponseDto httpResponseDto = new HttpResponseDto()                 {                     IsSuccess = false,                     Code = StatusCodes.Status403Forbidden,                     Message = "验证失败,请输入正确手机号及获取到的验证码"                 };                 var responJson = JsonConvert.SerializeObject(httpResponseDto);                 return StatusCode(StatusCodes.Status403Forbidden, responJson);             }         } 

Http调用:详细调用参数和方式见下方接口在线文档。
图形验证码+短信验证码实战

五、短信验证码获取与校验:

获取:

代码:

/// <summary>         /// 获取短信验证码         /// </summary>         /// <param name="msgCaptchaDto">短信验证码请求信息</param>         /// <returns></returns>         [HttpGet("msg")]         public IActionResult GetMsgCaptcha([FromQuery]MsgCaptchaDto msgCaptchaDto)         {             var msgSendResult = _captchaService.GetMsgCaptcha(msgCaptchaDto);             if (msgSendResult.Item1)             {                 return Ok(msgSendResult.Item2);             }             else             {                 return StatusCode(StatusCodes.Status403Forbidden, msgSendResult.Item2);             }         } 

Http调用:详细调用参数和方式见下方接口在线文档。
图形验证码+短信验证码实战

图形验证码+短信验证码实战

校验:

代码:

/// <summary>         /// 验证短信验证码         /// </summary>         /// <param name="msgCaptchaDto">短信验证码信息</param>         /// <returns></returns>         [HttpPost("msg")]         public IActionResult ValidateMsgCaptcha(MsgCaptchaDto msgCaptchaDto)         {             var validateResult = _captchaService.ValidateMsgCaptcha(msgCaptchaDto);             if (validateResult.Item1)             {                 HttpResponseDto httpResponseDto = new HttpResponseDto()                 {                     IsSuccess = true,                     Code = StatusCodes.Status200OK,                     Message = validateResult.Item2                 };                 var responJson = JsonConvert.SerializeObject(httpResponseDto);                 return Ok(responJson);             }             else             {                 HttpResponseDto httpResponseDto = new HttpResponseDto()                 {                     IsSuccess = false,                     Code = StatusCodes.Status403Forbidden,                     Message = validateResult.Item2                 };                 var responJson = JsonConvert.SerializeObject(httpResponseDto);                 return StatusCode(StatusCodes.Status403Forbidden, responJson);             }         } 

Http调用:详细调用参数和方式见下方接口在线文档。
图形验证码+短信验证码实战

源码链接地址:

Gitee完整实例地址:

https://gitee.com/mingliang_it/Captcha

接口在线文档:

链接地址:

https://console-docs.apipost.cn/preview/82c61b0950bae0c8/924487d25ec3df36

建群声明:本着技术在于分享,方便大家交流学习的初心,特此建立【编程内功修炼交流群】,热烈欢迎各位爱交流学习的程序员进群,也希望进群的大佬能不吝分享自己遇到的技术问题和学习心得。
图形验证码+短信验证码实战