Использование Google reCAPTCHA v2 в ASP.NET Core MVC приложении

  • Evgenia 

Если на вашем сайте имеются открытые (доступные анонимным пользователям) страницы с формами, отправляющими данные на сервер методом POST, то рано или поздно с них начнет приходить спам. Один из методов защиты от спам-ботов — использование капчи. В этой статье я расскажу, как интегрировать Google reCAPTCHA v2 в ASP.NET Core приложение без подключения сторонних библиотек.

Для примера было создано ASP.NET Core 2.1 приложение из стандартного шаблона Web Application (Model-View-Controller) без аутентификации, убран лишний код и добавлена простая форма отправки сообщения на страницу Contact. Ниже описано, как на эту форму была подключена капча. Исходный код приложения полностью выложен на GitHub.

Регистрация капчи и получение ключей

Для начала потребуется зарегистрировать капчу для нашего сайта. Сделать это можно по адресу: https://www.google.com/recaptcha/admin. Там все довольно просто: заполняем название капчи, выбираем ее тип, прописываем домен сайта. В этом примере используется тип капчи Checkbox. Для тестирования локально, добавьте в список доменов localhost:

После этого вы получите два ключа: открытый (Site key) и секретный (Secret key). Первый будет использоваться на клиенте (в представлении), второй — на сервере для проверки полученных с клиента данных:

Добавление ключей в настройки приложения

Теперь полученные ключи надо прописать в конфигурацию приложения, чтобы иметь к ним доступ из кода. Их можно добавить в appsettings.json, но лучше — в secrets.json:

{
  "ReCaptcha": {
    "SiteKey": "6LfIUnYU...1IHn1h",
    "SecretKey": "6LfIUnYU...6mT-c"
  }
}

Добавить настройки в менеджер секретов (User Secrets) можно через командную строку, хотя проще отредактировать файл secrets.json вручную. В Visual Studio 2017 его можно открыть через контекстное меню проекта в Solution Explorer командной Manage User Secrets.

Далее реализуем шаблон параметров (Options pattern). Добавим класс AppOptions. Класс ReCaptchaOptions нужен для объединения связанных настроек. Практика показывает, что когда настроек в приложении становится очень много, такое разбиение помогает не запутаться:

public class AppOptions
{
    public ReCaptchaOptions ReCaptcha { get; set; } 
}
public class ReCaptchaOptions
{
    public string SiteKey { get; set; }

    public string SecretKey { get; set; }
}

В метод ConfigureServices класса Startup добавляем строчку:

services.Configure(Configuration);

Добавление капчи на форму

Теперь перейдем в представление, содержащее форму отправки данных. Здесь потребуется использовать SiteKey из настроек. Чтобы его получить, сделаем внедрение зависимости в представление с помощью директивы @inject:

@inject IOptions AppOptions

Дальше, в место куда должна подставляться капча внутри формы, добавляем следующий код:


@Html.ValidationMessage("ReCaptchaError", new { @class = "text-danger" })

Валидацию пришлось вставлять старым способом без использования tag helper, так как свойство ReCaptchaError во ViewModel отсутствует.

И последнее важное изменение — добавить скрипт загрузки капчи на страницу:

@section Scripts {
    ...
    
}

Реализация проверки капчи на сервере

После нажатия на кнопку отправки сообщения будет выполнен POST-запрос. На сервере, помимо данных формы Email и Message, мы получим еще значение g-recaptcha-response, которое нужно будет проверить отправкой POST-запроса по адресу https://www.google.com/recaptcha/api/siteverify. Чтобы десериализовать JSON-результат этого запроса, добавляем класс RecaptchaResponse:

public class RecaptchaResponse
{
    [JsonProperty("success")]
    public bool Success { get; set; }

    [JsonProperty("error-codes")]
    public List ErrorCodes { get; set; }
}

Код проверки капчи вынесен в отдельный класс, чтобы в будущем его можно было переиспользовать:

public class GoogleRecaptchaService : IRecaptchaService
{
    private readonly ReCaptchaOptions _options;
    private readonly HttpClient _httpClient;

    public GoogleRecaptchaService(IOptions optionsAccessor)
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("https://www.google.com");

        _options = optionsAccessor.Value.ReCaptcha;
    }

    public async Task Validate(IFormCollection form)
    {
        var gRecaptchaResponse = form["g-recaptcha-response"];
        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair("secret", _options.SecretKey),
            new KeyValuePair("response", gRecaptchaResponse)
        });

        var response = await _httpClient.PostAsync("/recaptcha/api/siteverify", content);
        var resultContent = await response.Content.ReadAsStringAsync();
        var captchaResponse = JsonConvert.DeserializeObject(resultContent);

        return captchaResponse;
    }
}

Для получения доступа к сервису из контроллера, используем внедрение зависимостей. Для этого регистрируем сервис в методе ConfigureServices класса Startup:

services.AddSingleton();

и передаем экземпляр сервиса в контроллер:

public class HomeController : Controller
{
    private readonly IRecaptchaService _recaptcha;

    public HomeController(IRecaptchaService recaptcha)
    {
        _recaptcha = recaptcha;
    }

    // ...
}

После этого можно вызвать проверку капчи в методе POST:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task Contact(MessageViewModel model)
{
    if (ModelState.IsValid)
    {
        var captchaResponse = await _recaptcha.Validate(Request.Form);
        if (!captchaResponse.Success)
        {
            ModelState.AddModelError("reCaptchaError", 
                "reCAPTCHA error occured. Please try again.");
            return View(model);
        }

        // TODO: save data

        return RedirectToAction(nameof(Index));
    }

    return View(model);
}

Результат

После запуска приложения и загрузки страницы Contacts мы увидим подключенную капчу. В случае ее успешной проверки, сообщение будет отправлено и произойдет редирект на главную страницу. Если же капчу не разрешить (для этого нужно поставить галочку и, возможно, выполнить задания с картинками), появится ошибка:

Если при подключении капчи данным способом у вас возникли сложности, посмотрите исходный код приложения на GitHub или напишите мне свои вопросы в комментариях ниже.

Метки:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *