[ Полезный рекламный блок ]
Попробуйте свои силы в игре, где ваши навыки программирования на C# станут решающим фактором. Переходите по ссылке 🔰.
В этой статье мы рассмотрим процесс генерации кода Captcha в ASP.NET Core приложении, для формы обратной связи.
Если вы хотите использовать код Captcha для защиты вашего сайта от спама, есть несколько вариантов, таких как Google ReCaptcha и captcha.com. Оба они могут быть интегрированы в приложения ASP.NET Core. Однако по каким-то причинам вы все же можете захотеть сгенерировать код captcha самостоятельно. В этом уроке, я расскажу вам, как генерировать и использовать код captcha в последней версии ASP.NET Core.
Генерация кода Captcha в ASP.NET Core
Реализация кода captcha в ASP.NET Core 6
Как работает captcha
Простая captcha — это генерация случайного кода (цифр и букв), сохранение кода в сессии, а также создание изображения для показа на веб-странице. Когда пользователь отправляет содержимое обратно на сервер, сервер сравнивает введенную пользователем captcha со значением, сохраненным в сессии, чтобы проверить, правильно ли пользователь ввел код captcha. Рабочий процесс объясняется на рисунке ниже:
Рассмотрим образец captcha которую мы сделаем:
Реализация кода captcha в ASP.NET Core 6
Разобравшись, как работает код captcha, перейдем к ее реализации. Я ожидаю от вас знания концепций объектно-ориентированного программирования на C#. Я предполагаю, что вы также знаете концепции .NET Core, особенно паттерн MVC.
Откройте Visual Studio 2022.
Создайте проект «Asp.Net Core Web Application(.Net Core)» с шаблоном MVC Template. В графе “Authentication type”, оставляем значение None.
- Мы будем использовать классы из пространства имен System.Drawing.Imaging, поэтому нам также необходимо установить пакет NuGet:
1 |
Install-Package System.Drawing.Common -Version 4.5.1 |
- Поскольку captcha полагается на хранение сессий, нам также необходимо включить поддержку сессий в ASP.NET Core в файле Program.cs:
1 2 3 4 5 6 |
builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(20); options.Cookie.HttpOnly = true; }); app.UseSession(); |
3. В папке Model, создадим класс CaptchaResult для представления информации о captcha:
1 2 3 4 5 6 7 |
public class CaptchaResult { public string CaptchaCode { get; set; } public byte[] CaptchaByteData { get; set; } public string CaptchBase64Data => Convert.ToBase64String(CaptchaByteData); public DateTime Timestamp { get; set; } } |
4. Создадим папку Data, а в нее добавим новый класс Captcha для генерации и проверки captcha:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
public static class Captcha { const string Letters = "2346789ABCDEFGHJKLMNPRTUVWXYZ"; 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(); } public static bool ValidateCaptchaCode(string userInputCaptcha, HttpContext context) { var isValid = userInputCaptcha == context.Session.GetString("CaptchaCode"); context.Session.Remove("CaptchaCode"); return isValid; } public static CaptchaResult GenerateCaptchaImage(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); int maxY = height - fontSize; 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), 3); for (int i = 0; i < rand.Next(3, 5); 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(); } } } } |
Буквы содержат не все цифры и английский алфавит, потому что некоторые символы трудно различимы. Я закомментировал код “Безье” в функции DrawDisorderLine(), потому как изображение очень маленькое, будет трудно увидеть символы, если на нем есть линии Безье.
- Теперь в контроллере HomeController, создайте действие для возврата изображения captcha:
1 2 3 4 5 6 7 8 9 10 |
public IActionResult GetCaptchaImage() { int width = 100; int height = 36; var captchaCode = Captcha.GenerateCaptchaCode(); var result = Captcha.GenerateCaptchaImage(width, height, captchaCode); HttpContext.Session.SetString("CaptchaCode", result.CaptchaCode); Stream s = new MemoryStream(result.CaptchaByteData); return new FileStreamResult(s, "image/png"); } |
Попробуем получить доступ к этому действию, мы должны получить изображение captcha:
- Captcha настроена и готова к работе. В качестве примера, сделаем контактную форму для сайта с использованием captcha.
Создание контактной формы
- Установим зависимости, необходимые для отправки сообщения:
- MailKit
- MimeKit
- После установки библиотеки Mailkit, в папке Models, создадим новый класс SendMessage:
1 2 3 4 5 6 7 8 9 10 11 12 |
public class SendMessage { [Required(ErrorMessage = "Введите заголовок")] public string Title { get; set; } [Required(ErrorMessage = "Введите сообщение")] public string Description { get; set; } [Required(ErrorMessage = "Введите код капчи")] [StringLength(4)] public string CaptchaCode { get; set; } } |
Свойство CaptchaCode будет использоваться для отправки содержимого captcha на сервер.
- Перейдем к контроллеру HomeController, добавим действия Email, со следующим содержимым:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
[HttpGet] public IActionResult Email() { return View(); } [HttpPost] [AutoValidateAntiforgeryToken] public IActionResult Email(SendMessage sendMessage) { if (ModelState.IsValid) { //Проверяем код капчи if (!Captcha.ValidateCaptchaCode(sendMessage.CaptchaCode, HttpContext)) { ModelState.AddModelError("CaptchaCode", "Не верный код капчи"); return View(sendMessage); } var message = new MimeMessage(); //от кого отправляем и заголовок message.From.Add(new MailboxAddress("Письмо с контактной формы сайта", "mail@gmail.com")); //кому отправляем message.To.Add(new MailboxAddress("Admin", "admin@gmail.com")); //тема письма message.Subject = sendMessage.Title; //тело письма message.Body = new TextPart("plain") { Text = sendMessage.Description, }; using (var client = new SmtpClient()) { //Указываем smtp сервер почты и порт client.Connect("smtp.gmail.com", 587, false); //Указываем свой Email адрес и пароль приложения client.Authenticate("mail@gmail.com", "password"); client.Send(message); client.Disconnect(true); } return Content("Успешно отправлено!"); } return View(sendMessage); } |
Не забудьте изменить smtp сервер, email и пароль. Всю подробную информацию, про отправку письма, читайте в статье — «Отправка Email в Asp.Net Core«.
- В папку Views – Home, добавим представление Email.cshtml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
@model WebApplication1.Models.SendMessage @{ ViewData["Title"] = "Обратная связь"; } <div class="container col-xxl-8 px-4 py-5"> <div class="col-lg-6"> <h1 class="display-5 fw-bold lh-1 mb-3">Обратная связь</h1> <div class="justify-content-md-start"> <form method="post" asp-controller="Home" asp-action="Email"> <div asp-validation-summary="ModelOnly"></div> <div class="form-group"> <label asp-for="Title" class="control-label"></label> <input asp-for="Title" class="form-control" /> <span asp-validation-for="Title" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Description" class="control-label"></label> <textarea asp-for="Description" class="form-control"></textarea> <span asp-validation-for="Description" class="text-danger"></span> </div> <div class="form-group"> <div class="input-group-prepend"> <img id="img-captcha" src="/home/GetCaptchaImage" /> </div> <input type="text" class="form-control" asp-for="CaptchaCode" maxlength="4" /> <span asp-validation-for="CaptchaCode" class="text-danger"></span> </div> <div class="form-group"> @Html.AntiForgeryToken() <input type="submit" value="Войти" class="btn btn-primary" /> </div> </form> </div> </div> </div> @await Html.PartialAsync("_ValidationScriptsPartial") |
Запустим проект и перейдем по адресу: https://localhost:7221/home/email
Проверим работу captcha, введем неверный код:
Теперь введем верный код и выполним отправку письма:
Вы можете позволить пользователю загружать новое изображение captcha с помощью клика по ней. Для этого, внизу страницы Email.cshtml, добавим библиотеку JQuery:
1 |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> |
И следующий код:
1 2 3 4 5 6 7 8 9 10 |
<script> $("#img-captcha").click(function () { resetCaptchaImage(); }); function resetCaptchaImage() { d = new Date(); $("#img-captcha").attr("src", "/home/GetCaptchaImage?" + d.getTime()); } </script> |
На этом статья «Генерация кода Captcha в ASP.NET Core», подошла к концу, надеюсь вам было интересно.
Поделитесь вашим опытом в комментариях, как вы генерируете код captcha в ASP.NET Core приложениях?
Так же вам может быть интересна предыдущая статья:
Вы начинающий программист, который хочет изучить все тонкости языка C#?
Пройдите наш тест на 13 вопросов, чтобы узнать, как много вы знаете на самом деле!
C# Braincheck |
Вы хотите научится писать код на языке программирования C#?
Создавать различные информационные системы, состоящие из сайтов, мобильных клиентов, десктопных приложений, телеграмм-ботов и т.д.
Переходите к нам на страницу Dijix и ознакомьтесь с условиями обучения, мы специализируемся только на индивидуальных занятиях, как для начинающих, так и для более продвинутых программистов. Вы можете взять как одно занятие для проработки интересующего Вас вопроса, так и несколько, для более плотной работы. Благодаря личному кабинету, каждый студент повысит качество своего обучения, в вашем распоряжении:
- Доступ к пройденному материалу
- Тематические статьи
- Библиотека книг
- Онлайн тестирование
- Общение в закрытых группах
Живи в своем мире, программируй в нашем.