如何在 ASP.NET MVC 中实现 reCaptcha?

如何在 ASP.NET MVC 和 C # 中实现 reCaptcha?

118534 次浏览

这里有几个很好的例子:

这个问题以前也在 这个堆栈溢出问题中讨论过。

用于 MVC 4和5的 NuGet Google reCAPTCHA V2

我以下面的方式成功地实现了 ReCaptcha。
注意: 这是在 VB 中,但可以很容易地转换

首先抓取一个 reCaptcha 库的副本

2] 然后构建一个自定义 ReCaptcha HTML Helper

    ''# 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

从这里开始,您需要一个可重用的服务器端验证器

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

我已经将 reCaptcha 添加到我目前正在进行的一个项目中。当 reCaptcha 元素被动态加载到页面中时,我需要它来使用 AJAXAPI。我找不到任何现有的控件,而且 API 很简单,所以我创建了自己的。

我会把我的代码贴在这里,以防有人觉得它有用。

1: 将 script 标记添加到母版页头

<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: 创建 Action 属性和 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();

对于其他正在寻找的人,这里有一套不错的步骤

不要忘记像我一样在 OnActionExecting ()中手动添加密钥。

步骤1: 客户端站点集成

在 HTML 模板的结束 </head>标记之前粘贴这个片段:

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

将这个代码片段粘贴到您希望 reCAPTCHA 小部件出现的 <form>末尾:

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

步骤2: 服务器站点集成

当您的用户提交您集成 reCAPTCHA 的表单时,您将获得一个名为“ g-captcha-response”的字符串作为有效负载的一部分。为了检查 Google 是否已经验证了该用户,请发送带有以下参数的 POST 请求:

网址: https://www.google.com/recaptcha/api/siteverify

秘密: 你的秘密钥匙

响应: “ g- 重演-响应”的价值。

现在开始你的 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 :)
}

简单完整的解决方案 为我工作。 支持 ASP.NET MVC 4和5(支持 ASP.NET 4.0、4.5和4.5.1)

步骤1: 按“ 安装-包 reCAPTCH.MVC”安装 NuGet 包

步骤2: 将公钥和私钥添加到 apptings 部分的 web.config 文件中

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

你可以为你的网站创建一个 API 密钥对,地址是: http://www.google.com/captcha/intro/index.html”rel = “ norefrer”> https://www.google.com/recaptcha/intro/index.html 然后点击页面顶部的 Get reCAPTCHA

步骤3: 修改表单以包含 reCaptcha

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

步骤4 : 实现 Controller Action,它将处理表单提交和验证验证

[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
{
}
}

扩展 Magpie 的答案,下面是我在项目中使用的动作过滤器代码。

它与 ASP 核心 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"
}
);
}

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>