|
前言:
上一篇分分享了基于阿里云实现的短信验证码文章,考虑到为了防止登录时,非人工操作,频繁获取验证码,趁热打铁,现在添加了图片验证码服务功能。借鉴网上传统的做法,把实现这两个验证的功能做成有个独立的服务,通过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
建群声明:本着技术在于分享,方便大家交流学习的初心,特此建立【编程内功修炼交流群】,热烈欢迎各位爱交流学习的程序员进群,也希望进群的大佬能不吝分享自己遇到的技术问题和学习心得。
来源:https://www.cnblogs.com/wml-it/archive/2023/08/09/17617097.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|