ASP.NET Core - Implementando Captcha


Neste artigo veremos como implementar o Captcha em uma aplicação ASP .NET Core.

Segundo a Wikipédia CAPTCHA é um acrônimo da expressão "Completely Automated Public Turing test to tell Computers and Humans Apart" que traduzido livremente seria algo como "Teste de Turing público completamente automatizado para diferenciação entre computadores e humanos": sendo um teste de desafio cognitivo, utilizado como ferramenta antispam, desenvolvido de forma pioneira na universidade de Carnegie-Mellon.

Assim o CAPTCHA funciona como um teste de Turing reverso onde uma máquina propõe um pergunta que somente um ser humano é capaz de responder. (isso é o que se espera...)

Implementando o CAPTCHA na ASP .NET Core

Vamos fazer a implementação deste recurso em um projeto ASP .NET Core MVC usando o pacote DNTCaptcha.Core distribuído via Nuget.

Abra o VS 2019 Community e clique em New Project  e Selecione o template ASP .NET Core Web API e clique em Next;

Informe o nome WebCaptcha e clique em Next;

A seguir selecione o Target Framework, Authentication Type e demais configurações conforme mostrada na figura:

A seguir vamos incluir a referência ao pacote Nuget acionando o menu Tools e clicando em Manage Nuget Packages for Solution e a seguir na guia Browse e informando o nome DNTCaptcha.Core e selecionando a última versão estável e instalando o pacote no projeto:

A seguir temos que incluir a TagHelper no arquivo _ViewImports.cshtml na pasta Views:

@using WebCaptcha
@using WebCaptcha.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, DNTCaptcha.Core

Agora precisamos registrar o serviço DNTCaptcah.Core no arquivo Startup e para isso temos as seguintes opções de provedores:

  1. SessionStorageProvider
  2. MemoryCacheStorageProvider
  3. CookieStorageProvider
  4. DistributedCacheStorageProvider

E além disso podemos também configurar opções como Expiração e Separadores.

Para o exemplo usado neste artigo vamos usar o provider : CookieStorageProvider

public void ConfigureServices(IServiceCollection services)
 {
      services.AddControllersWithViews();

      services.AddDNTCaptcha(options =>
          options.UseCookieStorageProvider()
              .ShowThousandsSeparators(false)
          );
 }

Com isso poderemos aplicar e realizar a validação do CAPTCHA e para isso temos dois métodos:

  1. Usar o atributo ValidateDNTCaptcha
  2. Usar o serviço IDNTCaptchaValidatorService

Criando o Controller, a ViewModel e a View

Agora vamos criar na pasta Models do projeto a viewmodel LoginViewModel com o código abaixo:

public class LoginViewModel
{
[Required(ErrorMessage = "Username Required")]
public string Username { get; set; }

[Required(ErrorMessage = "Password Required")]
public string Password { get; set; }
}

Após isso vamos iniciar o teste usando o atributo ValidateDNTCaptcha  e para isso vamos criar no controlador HomeController o método Action Login para Get e Post :

using DNTCaptcha.Core;
using Microsoft.AspNetCore.Mvc;
using WebCaptcha.Models;
namespace WebCaptcha.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
        public IActionResult Privacy()
        {
            return View();
        }
        [HttpGet]
        public IActionResult Login()
        {
            return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        [ValidateDNTCaptcha(
            ErrorMessage = "Please Enter Valid Captcha",
            CaptchaGeneratorLanguage = Language.English,
            CaptchaGeneratorDisplayMode = DisplayMode.SumOfTwoNumbers)]
        public IActionResult Login(LoginViewModel loginViewModel)
        {
            if (ModelState.IsValid)
            {
                return RedirectToAction("Index", "Home");
            }
            return View();
        }
    }
}

No método Action Post estamos aplicando o atributo ValidateDNTCaptcha e definindo a mensagem de erro a ser exibida, o idioma usado e o modo de exibição do CAPTCHA, onde definimos a opção SumOfTwoNumbers que vai apresentar o desafio para o usuário somar dois números. Além destas opções temos as seguintes opções :

Agora precisamos criar a view Login.cshtml na pasta Views/Home e aqui é onde precisamos definir a tag-helper dnt-captcha.

Antes de criar View vejamos as opções de configuração da tag-helper dnt-captcha :

<dnt-captcha 
asp-captcha-generator-max="999999"
asp-captcha-generator-min="111111"
asp-captcha-generator-language="English"
asp-captcha-generator-display-mode="ShowDigits"
asp-use-relative-urls="true"
asp-placeholder="Enter Security code"
asp-validation-error-message="Please enter the security code."
asp-font-name="Tahoma"
asp-font-size="20"
asp-fore-color="#333333"
asp-back-color="#ccc"
asp-text-box-class="text-box form-control"
asp-text-box-template="<span class='input-group-prepend'><span class='form-group-text'></span></span>{0}"
asp-validation-message-class="text-danger"
asp-refresh-button-class="fas fa-redo btn-sm"
asp-use-noise="false" />

Aqui temos as seguintes opções de configuração :

  1. Podemos configurar o intervalo de números que será gerado
  2. O idioma usado
  3. A opção para exibir o Captcha
  4. A mensagem padrão
  5. A mensagem de erro a ser exibida na validação
  6. A fonte usada
  7. A cor de fundo
  8. O template para o controle do formulário

Para definir o botão Refresh/Atualizar você poderá diversas as fontes awewsome ou glyphicon.

Para fazer o botão de atualização do Captcha funcionar, precisamos adicionar a biblioteca jquery.unobtrusive-ajax pois a DNTCaptcha.Core usa esta biblioteca. Você pode baixar a biblioteca neste link: https://github.com/aspnet/jquery-ajax-unobtrusive/releases.

Após baixar copie o arquivo jquery.unobtrusive-ajax.js para a pasta wwwroot/js do projeto.

A seguir temos o código para view Login.cshtml :

@model LoginViewModel
@{
    Layout = null;
}

<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css" rel="stylesheet">

<div class="container">
    <br />
    <div class="col-md-4">
        <div class="card">
            <div class="card-body">

                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <form id="account" asp-controller="Home" asp-antiforgery="true" asp-action="Login" method="post">

                    <span asp-validation-for="Username" class="text-danger"></span>
                    <div class="input-group mb-3">
                        <input asp-for="Username" class="form-control" placeholder="UserName" />
                    </div>

                    <span asp-validation-for="Password" class="text-danger"></span>
                    <div class="input-group mb-3">
                        <input type="password" asp-for="Password" autocomplete="new-password"
                               class="form-control" placeholder="Password" />

                    </div>
                    <div class="input-group mb-3">

                        <dnt-captcha asp-captcha-generator-max="999999"
                                     asp-captcha-generator-min="111111"
                                     asp-captcha-generator-language="English"
                                     asp-captcha-generator-display-mode="SumOfTwoNumbers"
                                     asp-use-relative-urls="true"
                                     asp-placeholder="Enter Captcha"
                                     asp-validation-error-message="Please enter the security code."
                                     asp-font-name="Tahoma"
                                     asp-font-size="20"
                                     asp-fore-color="#333333"
                                     asp-back-color="#ccc"
                                     asp-text-box-class="text-box form-control"
                                     asp-text-box-template="<span class='input-group-prepend'>
                                     <span class='form-group-text'></span></span>{0}"
                                     asp-validation-message-class="text-danger"
                                     asp-refresh-button-class="fas fa-redo btn-sm"
                                     asp-use-noise="false" />
                    </div>
                       <div class="row">
                          <div class="col-8">
                        </div>
                            <div class="col-4">
                               <button type="submit" id="btnsubmit" class="btn btn-primary btn-block">Sign In</button>
                           </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
<script src="~/js/jquery.unobtrusive-ajax.js"></script>

Agora podemos testar o CAPTCHA usando a validação via atributo:

A outra opção é realizar a validação usando o serviço IDNTCaptchaValidatorService. Para usar essa abordagem vamos criar um novo controlador chamado AccountController com o código abaixo:

using DNTCaptcha.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
using WebCaptcha.Models;
namespace WebCaptcha.Controllers
{
    public class AccountController : Controller
    {
        private readonly IDNTCaptchaValidatorService _validatorService;
        private readonly DNTCaptchaOptions _captchaOptions;
        public AccountController(IDNTCaptchaValidatorService validatorService,
            IOptions<DNTCaptchaOptions> options)
        {
            _validatorService = validatorService;
            _captchaOptions = options == null ? 
                throw new ArgumentNullException(nameof(options)) : options.Value;
        }
        [HttpGet]
        public IActionResult Login()
        {
            return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Login(LoginViewModel loginViewModel)
        {
            if (ModelState.IsValid)
            {
                if (!_validatorService.HasRequestValidCaptchaEntry(Language.English, DisplayMode.ShowDigits))
                {
                    this.ModelState.AddModelError(_captchaOptions.CaptchaComponent.CaptchaInputName,
                        "Please enter a valid security code.");
                    return View(nameof(Login));
                }
            }
            return View();
        }
    }
}

Teremos que criar a view Login.cshtml na pasta Account do projeto cujo código será idêntico à view Login usada para o controlador HomeController.

Somente iremos definir outro modo de exibição para o CAPTCHA usando o modo: ShowDigits

Testando para esta opção temos o resultado a seguir:

Pegue o código do projeto aqui :  WebCaptcha.zip

"Eu rogo por eles; não rogo pelo mundo, mas por aqueles que me deste, porque são teus.
E todas as minhas coisas são tuas, e as tuas coisas são minhas; e neles sou glorificado."
João 17:9,10

Referências:


José Carlos Macoratti