如何实现ASP.NET MVC的reCaptcha?

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

有几个很好的例子:

  • Derik Whittaker 使用ASP.NET MVC的ReCaptcha
  • MVC reCaptcha – 使reCaptcha更多MVC'ish。
  • ASP.NET MVC 3中的ReCaptcha Webhelper
  • 来自Google Code的ASP.NET MVC的ReCaptcha控件 。

这也已经在这个堆栈溢出问题之前被覆盖。

NuGet Google reCAPTCHA V2 for MVC 4和5

  • NuGet包
  • 演示和文件

我已经将reCaptcha添加到我目前正在进行的项目中。 我需要它使用AJAX API,因为reCaptcha元素是dynamic加载到页面中的。 我找不到任何现有的控件,API很简单,所以我创build了自己的。

我会在这里发布我的代码,以防有人发现它有用。

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:创buildAction属性和Html助手扩展

 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:添加validation码到您的视图

 @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:注意你需要在每个post后重新加载validation码,即使它是有效的,而表单的另一部分是无效的。 使用Recaptcha.reload();

我已经通过以下方式成功实施了ReCaptcha。
注意:这是在VB中,但可以很容易地转换

1]首先抓取reCaptcha库的副本

2]然后构build一个自定义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]从这里你需要一个可重用的服务器端validation器

 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的容易程度

现在,你有你的可重用的代码…所有你需要做的是添加validation码到你的视图。

 <%: 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 

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

步骤1:通过“ Install-Package reCAPTCH.MVC ”安装NuGet包

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

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

您可以在https://www.google.com/recaptcha/intro/index.html上为您的网站创buildAPI密钥对,然后点击页面顶部的获取reCAPTCHA

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

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

步骤4 :实现将处理表单提交和validation码validation的控制器操作

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

第1步:客户站点集成

将这个代码片段粘贴到HTML模板上的closures</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-recaptcha-response”的string的一部分。 为了检查Google是否validation了该用户,请使用以下参数发送POST请求:

url: https : //www.google.com/recaptcha/api/siteverify

秘密:你的秘密钥匙

回应:“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 :) } 

对于其他人看,这是一个体面的一套步骤。 http://forums.asp.net/t/1678976.aspx/1

不要忘记像我一样手动添加您的密钥在OnActionExecuting()。

扩展Magpie的答案,这里是我在我的项目中使用的动作filter的代码。

它适用于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" } ); } 

MVC 5的asynchronous版本(即避免ActionFilterAttribute,在MVC 6之前不是asynchronous)和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>