Як реалізувати reCaptcha для ASP.NET MVC? [зачинено]


80

Як реалізувати reCaptcha в ASP.NET MVC та C #?


Якщо ви шукаєте останнє рішення комбінованого Google reCAPTCHA v2 та v3 у веб-формах ASP.NET, перевірте демо-версію techtolia.com/Recaptcha
Лев,

Відповіді:


83

Є кілька чудових прикладів:

Це також висвітлювалось раніше в цьому питанні щодо переповнення стека .

NuGet Google reCAPTCHA V2 для MVC 4 і 5


дякую за швидку відповідь. У мене питання, хто створив Recaptcha.dll? Команда Google?
xport

4
ReCaptcha був створений професором Університету Карнегі-Меллона, на мою думку.
Джордж Стокер

1
Третій ( dotnetcurry.com/ShowArticle.aspx?ID=611 ) для мене чудово працював.
seldary

Я використовую код Діріка Віттейкера. Посилання на Microsoft.Web.Helpers, але я отримую ім'я простору імен 'Recaptcha' не знайдено помилку в цьому рядку: var captchaValidtor = new
Recaptcha.RecaptchaValidator

@ M3NTA7 Можливо, ви захочете поставити запитання, яке заглиблюється трохи глибше, ніж цей коментар.
Джордж Стокер

33

Я додав reCaptcha до проекту, над яким я зараз працюю. Мені він знадобився для використання API AJAX, оскільки елемент reCaptcha динамічно завантажувався на сторінку. Я не міг знайти жодного існуючого елемента керування, а API простий, тому я створив власний.

Я розміщу тут свій код, якщо хтось вважатиме це корисним.

1: Додайте тег сценарію до заголовків головної сторінки

<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>

2: Додайте ключі до web.config

<appSettings>
    <add key="ReCaptcha.PrivateKey" value="[key here]" />
    <add key="ReCaptcha.PublicKey" value="[key here]" />
</appSettings>

3: Створіть розширення Атрибут дії та Html Helper

namespace [Your chosen namespace].ReCaptcha
{
    public enum Theme { Red, White, BlackGlass, Clean }

    [Serializable]
    public class InvalidKeyException : ApplicationException
    {
        public InvalidKeyException() { }
        public InvalidKeyException(string message) : base(message) { }
        public InvalidKeyException(string message, Exception inner) : base(message, inner) { }
    }

    public class ReCaptchaAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var userIP = filterContext.RequestContext.HttpContext.Request.UserHostAddress;

            var privateKey = ConfigurationManager.AppSettings.GetString("ReCaptcha.PrivateKey", "");

            if (string.IsNullOrWhiteSpace(privateKey))
                throw new InvalidKeyException("ReCaptcha.PrivateKey missing from appSettings");

            var postData = string.Format("&privatekey={0}&remoteip={1}&challenge={2}&response={3}",
                                         privateKey,
                                         userIP,
                                         filterContext.RequestContext.HttpContext.Request.Form["recaptcha_challenge_field"],
                                         filterContext.RequestContext.HttpContext.Request.Form["recaptcha_response_field"]);

            var postDataAsBytes = Encoding.UTF8.GetBytes(postData);

            // Create web request
            var request = WebRequest.Create("http://www.google.com/recaptcha/api/verify");
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = postDataAsBytes.Length;
            var dataStream = request.GetRequestStream();
            dataStream.Write(postDataAsBytes, 0, postDataAsBytes.Length);
            dataStream.Close();

            // Get the response.
            var response = request.GetResponse();

            using (dataStream = response.GetResponseStream())
            {
                using (var reader = new StreamReader(dataStream))
                {
                    var responseFromServer = reader.ReadToEnd();

                    if (!responseFromServer.StartsWith("true"))
                        ((Controller)filterContext.Controller).ModelState.AddModelError("ReCaptcha", "Captcha words typed incorrectly");
                }
            }
        }
    }

    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString GenerateCaptcha(this HtmlHelper helper, Theme theme, string callBack = null)
        {
            const string htmlInjectString = @"<div id=""recaptcha_div""></div>
<script type=""text/javascript"">
    Recaptcha.create(""{0}"", ""recaptcha_div"", {{ theme: ""{1}"" {2}}});
</script>";

            var publicKey = ConfigurationManager.AppSettings.GetString("ReCaptcha.PublicKey", "");

            if (string.IsNullOrWhiteSpace(publicKey))
                throw new InvalidKeyException("ReCaptcha.PublicKey missing from appSettings");

            if (!string.IsNullOrWhiteSpace(callBack))
                callBack = string.Concat(", callback: ", callBack);

            var html = string.Format(htmlInjectString, publicKey, theme.ToString().ToLower(), callBack);
            return MvcHtmlString.Create(html);
        }
    }
}

4: Додайте капчу до свого перегляду

@using (Html.BeginForm("MyAction", "MyController"))
{
   @Html.TextBox("EmailAddress", Model.EmailAddress)
   @Html.GenerateCaptcha(Theme.White)
   <input type="submit" value="Submit" />
}

5: Додайте атрибут до своєї дії

[HttpPost]
[ReCaptcha]
public ActionResult MyAction(MyModel model)
{
   if (!ModelState.IsValid) // Will have a Model Error "ReCaptcha" if the user input is incorrect
      return Json(new { capthcaInvalid = true });

   ... other stuff ...
}

6: Зверніть увагу: вам потрібно буде перезавантажити капчу після кожного повідомлення, навіть якщо воно було дійсним, а інша частина форми була недійсною. ВикористовуйтеRecaptcha.reload();


Чи можливо за допомогою цього коду анотувати ReCaptcha лише після 5 невдалих входів?
Роб

2
Якщо ви впроваджуєте це рішення, врахуйте, що новою URL-адресою API є: google.com/recaptcha/api/siteverify ( джерело )
Axel Prieto

14

Просте і повне рішення, яке працює для мене. Підтримує ASP.NET MVC 4 і 5 (підтримує ASP.NET 4.0, 4.5 та 4.5.1)

Крок 1: Встановіть пакет NuGet за допомогою " Install-Package reCAPTCH.MVC "

Крок 2: Додайте свій відкритий та приватний ключ до свого файлу web.config у розділі налаштувань програм

<appSettings>
    <add key="ReCaptchaPrivateKey" value=" -- PRIVATE_KEY -- " />
    <add key="ReCaptchaPublicKey" value=" -- PUBLIC KEY -- " />
</appSettings>  

Ви можете створити пару ключів API для свого веб-сайту за адресою https://www.google.com/recaptcha/intro/index.html і натисніть кнопку Отримати reCAPTCHA у верхній частині сторінки

Крок 3: Змініть форму, щоб включити reCaptcha

@using reCAPTCHA.MVC
@using (Html.BeginForm())
{
    @Html.Recaptcha()
    @Html.ValidationMessage("ReCaptcha")
    <input type="submit" value="Register" />
}

Крок 4 : Застосуйте дію контролера, яка обробляє подання форми та перевірку Captcha

[CaptchaValidator(
PrivateKey = "your private reCaptcha Google Key",
ErrorMessage = "Invalid input captcha.",
RequiredMessage = "The captcha field is required.")]
public ActionResult MyAction(myVM model)
{
    if (ModelState.IsValid) //this will take care of captcha
    {
    }
}

АБО

public ActionResult MyAction(myVM model, bool captchaValid)
{
    if (captchaValid) //manually check for captchaValid 
    {
    }
}

На жаль, неможливо використовувати його у двох різних формах на одній сторінці через Uncaught Error: ReCAPTCHA placeholder element must be empty.
Аліссон,

1
Оскільки reCaptcha використовує web.config для ключів, вам не потрібно вводити дані PrivateKey = "your private reCaptcha Google Key". Це набагато простіше, коли у вас є різні ключі для різних середовищ
Червоний

13

Асинхронна версія для MVC 5 (тобто уникаючи ActionFilterAttribute, який не є асинхронним до MVC 6) та reCAPTCHA 2

ExampleController.cs

public class HomeController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> ContactSubmit(
        [Bind(Include = "FromName, FromEmail, FromPhone, Message, ContactId")]
        ContactViewModel model)
    {
        if (!await RecaptchaServices.Validate(Request))
        {
            ModelState.AddModelError(string.Empty, "You have not confirmed that you are not a robot");
        }
        if (ModelState.IsValid)
        {
           ...

ExampleView.cshtml

@model MyMvcApp.Models.ContactViewModel

@*This is assuming the master layout places the styles section within the head tags*@
@section Styles {
    @Styles.Render("~/Content/ContactPage.css")
    <script src='https://www.google.com/recaptcha/api.js'></script>
}

@using (Html.BeginForm("ContactSubmit", "Home",FormMethod.Post, new { id = "contact-form" }))
{
    @Html.AntiForgeryToken()
    ...
    <div class="form-group">
      @Html.LabelFor(m => m.Message) 
      @Html.TextAreaFor(m => m.Message, new { @class = "form-control", @cols = "40", @rows = "3" })
      @Html.ValidationMessageFor(m => m.Message)
    </div>

    <div class="row">
      <div class="g-recaptcha" data-sitekey='@System.Configuration.ConfigurationManager.AppSettings["RecaptchaClientKey"]'></div>
    </div>

    <div class="row">
      <input type="submit" id="submit-button" class="btn btn-default" value="Send Your Message" />
    </div>
}

RecaptchaServices.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web;
using System.Configuration;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using System.Runtime.Serialization;

namespace MyMvcApp.Services
{
    public class RecaptchaServices
    {
        //ActionFilterAttribute has no async for MVC 5 therefore not using as an actionfilter attribute - needs revisiting in MVC 6
        internal static async Task<bool> Validate(HttpRequestBase request)
        {
            string recaptchaResponse = request.Form["g-recaptcha-response"];
            if (string.IsNullOrEmpty(recaptchaResponse))
            {
                return false;
            }
            using (var client = new HttpClient { BaseAddress = new Uri("https://www.google.com") })
            {
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                var content = new FormUrlEncodedContent(new[]
                {
                    new KeyValuePair<string, string>("secret", ConfigurationManager.AppSettings["RecaptchaSecret"]),
                    new KeyValuePair<string, string>("response", recaptchaResponse),
                    new KeyValuePair<string, string>("remoteip", request.UserHostAddress)
                });
                var result = await client.PostAsync("/recaptcha/api/siteverify", content);
                result.EnsureSuccessStatusCode();
                string jsonString = await result.Content.ReadAsStringAsync();
                var response = JsonConvert.DeserializeObject<RecaptchaResponse>(jsonString);
                return response.Success;
            }
        }

        [DataContract]
        internal class RecaptchaResponse
        {
            [DataMember(Name = "success")]
            public bool Success { get; set; }
            [DataMember(Name = "challenge_ts")]
            public DateTime ChallengeTimeStamp { get; set; }
            [DataMember(Name = "hostname")]
            public string Hostname { get; set; }
            [DataMember(Name = "error-codes")]
            public IEnumerable<string> ErrorCodes { get; set; }
        }

    }
}

web.config

<configuration>
  <appSettings>
    <!--recaptcha-->
    <add key="RecaptchaSecret" value="***secret key from https://developers.google.com/recaptcha***" />
    <add key="RecaptchaClientKey" value="***client key from https://developers.google.com/recaptcha***" />
  </appSettings>
</configuration>

Це спрацювало для мене! ... MVC v5, recaptcha v2 ~ 2018
MTAdmin 02.03.18

8

Крок 1: Інтеграція сайту клієнта

Вставте цей фрагмент перед закриваючим </head>тегом у ваш шаблон HTML:

<script src='https://www.google.com/recaptcha/api.js'></script>

Вставте цей фрагмент в кінець місця, <form>де ви хочете, щоб відображався віджет reCAPTCHA:

<div class="g-recaptcha" data-sitekey="your-site-key"></div>

Крок 2: Інтеграція з сервером

Коли ваші користувачі надсилають форму, де ви інтегрували reCAPTCHA, ви отримаєте як частину корисного набору рядок із назвою "g-recaptcha-response". Щоб перевірити, чи підтвердив Google цього користувача, надішліть запит POST із цими параметрами:

URL-адреса: https://www.google.com/recaptcha/api/siteverify

секрет: ваш секретний ключ

response: Значення 'g-recaptcha-response'.

Зараз у дії ваш додаток MVC:

// return ActionResult if you want
    public string RecaptchaWork()
    {
        // Get recaptcha value
        var r = Request.Params["g-recaptcha-response"];
        // ... validate null or empty value if you want
        // then
        // make a request to recaptcha api
        using (var wc = new WebClient())
        {
            var validateString = string.Format(
                "https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}",
               "your_secret_key",    // secret recaptcha key
               r); // recaptcha value
             // Get result of recaptcha
            var recaptcha_result = wc.DownloadString(validateString);
            // Just check if request make by user or bot
            if (recaptcha_result.ToLower().Contains("false"))
            {
                 return "recaptcha false";
            }
        }
        // Do your work if request send from human :)
    }

7

Я успішно реалізував ReCaptcha наступним чином.
Примітка: це у VB, але може бути легко перетворено

1] Спочатку візьміть копію бібліотеки reCaptcha

2] Потім створіть власний помічник ReCaptcha HTML

    ''# fix SO code coloring issue.
    <Extension()>
    Public Function reCaptcha(ByVal htmlHelper As HtmlHelper) As MvcHtmlString
        Dim captchaControl = New Recaptcha.RecaptchaControl With {.ID = "recaptcha",
                                                                  .Theme = "clean",
                                                                  .PublicKey = "XXXXXX",
                                                                  .PrivateKey = "XXXXXX"}
        Dim htmlWriter = New HtmlTextWriter(New IO.StringWriter)
        captchaControl.RenderControl(htmlWriter)
        Return MvcHtmlString.Create(htmlWriter.InnerWriter.ToString)
    End Function

3] Звідси вам потрібен повторно використовуваний валідатор на стороні сервера

Public Class ValidateCaptchaAttribute : Inherits ActionFilterAttribute
    Private Const CHALLENGE_FIELD_KEY As String = "recaptcha_challenge_field"
    Private Const RESPONSE_FIELD_KEY As String = "recaptcha_response_field"

    Public Overrides Sub OnActionExecuting(ByVal filterContext As ActionExecutingContext)

        If IsNothing(filterContext.HttpContext.Request.Form(CHALLENGE_FIELD_KEY)) Then
            ''# this will push the result value into a parameter in our Action
            filterContext.ActionParameters("CaptchaIsValid") = True
            Return
        End If

        Dim captchaChallengeValue = filterContext.HttpContext.Request.Form(CHALLENGE_FIELD_KEY)
        Dim captchaResponseValue = filterContext.HttpContext.Request.Form(RESPONSE_FIELD_KEY)

        Dim captchaValidtor = New RecaptchaValidator() With {.PrivateKey = "xxxxx",
                                                                       .RemoteIP = filterContext.HttpContext.Request.UserHostAddress,
                                                                       .Challenge = captchaChallengeValue,
                                                                       .Response = captchaResponseValue}

        Dim recaptchaResponse = captchaValidtor.Validate()

        ''# this will push the result value into a parameter in our Action
        filterContext.ActionParameters("CaptchaIsValid") = recaptchaResponse.IsValid

        MyBase.OnActionExecuting(filterContext)
    End Sub

над цим рядком можна повторно використати ** ОДИН РАЗ ** код


нижче цього рядка - як легко впроваджувати reCaptcha знову і знову

Тепер, коли у вас є ваш повторно використовуваний код ... все, що вам потрібно зробити, - це додати капчу до вашого представлення.

<%: Html.reCaptcha %>

І коли ви розміщуєте форму на своєму контролері ...

    ''# Fix SO code coloring issues
    <ValidateCaptcha()>
    <AcceptVerbs(HttpVerbs.Post)>
    Function Add(ByVal CaptchaIsValid As Boolean, ByVal [event] As Domain.Event) As ActionResult


        If Not CaptchaIsValid Then ModelState.AddModelError("recaptcha", "*")


        '#' Validate the ModelState and submit the data.
        If ModelState.IsValid Then
            ''# Post the form
        Else
            ''# Return View([event])
        End If
    End Function

Примітка: це у VB, але його можна легко перетворити на C #
Чейз Флорелл

1
велике спасибі за розробку рішення. Це дуже детальна відповідь. Я це люблю.
xport

1
Відповідно до останнього блогу Джоеля, творці StackOverflow хочуть, щоб це було сховище Wiki, а не просто перенаправляло користувачів на інші сайти. Відправлення вас на сайт може відповісти на ваше запитання сьогодні, але не вирішить чужу проблему, коли ця зовнішня сторінка з’явиться наступного року. Розміщення правильної відповіді тут також допоможе майбутнім шукачам. Я вважаю, що це правильний спосіб відповісти на питання.
Chase Florell

Виглядає майже подібно до devlicio.us/blogs/derik_whittaker/archive/2008/12/02/…, за винятком мовної різниці ..
користувач gmail

2

Розширюючи відповідь Сороки, ось код для фільтру дій, який я використовую у своєму проекті.

Це працює з ASP Core RC2!

public class ReCaptchaAttribute : ActionFilterAttribute
{
    private readonly string CAPTCHA_URL = "https://www.google.com/recaptcha/api/siteverify";
    private readonly string SECRET = "your_secret";

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        try
        {
            // Get recaptcha value
            var captchaResponse = filterContext.HttpContext.Request.Form["g-recaptcha-response"];

            using (var client = new HttpClient())
            {
                var values = new Dictionary<string, string>
                {
                    { "secret", SECRET },
                    { "response", captchaResponse },
                    { "remoteip", filterContext.HttpContext.Request.HttpContext.Connection.RemoteIpAddress.ToString() }
                };


                var content = new FormUrlEncodedContent(values);

                var result = client.PostAsync(CAPTCHA_URL, content).Result;

                if (result.IsSuccessStatusCode)
                {
                    string responseString = result.Content.ReadAsStringAsync().Result;

                    var captchaResult = JsonConvert.DeserializeObject<CaptchaResponseViewModel>(responseString);

                    if (!captchaResult.Success)
                    {
                        ((Controller)filterContext.Controller).ModelState.AddModelError("ReCaptcha", "Captcha not solved");
                    }
                } else
                {
                    ((Controller)filterContext.Controller).ModelState.AddModelError("ReCaptcha", "Captcha error");
                }
            }

        }
        catch (System.Exception)
        {
            ((Controller)filterContext.Controller).ModelState.AddModelError("ReCaptcha", "Unknown error");
        }
    }
}

І використовуйте його у своєму коді, як

[ReCaptcha]
    public IActionResult Authenticate()
    {

        if (!ModelState.IsValid)
        {
            return View(
                "Login",
                new ReturnUrlViewModel
                {
                    ReturnUrl = Request.Query["returnurl"],
                    IsError = true,
                    Error = "Wrong reCAPTCHA"
                }
            );
        }

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.