Mã nguồn ví dụ BotDetect ASP.NET MVC CAPTCHA dùng C#

Mã nguồn ví dụ BotDetect ASP.NET MVC hướng dẫn cách sử dụng BotDetect ASP.NET CAPTCHA trong ứng dụng ASP.NET MVC. Bắt đầu với dự án chuẩn ASP.NET MVC được tạo bởi Visual Studio (File > New Project > Visual C# > Web > ASP.NET MVC Web Application), chúng ta chỉnh sửa trang Register.aspx để sử dụng CAPTCHA trước khi cho phép người sử dụng đăng ký, đảm bảo rằng chỉ có con người mới tạo được tài khoản.

Vì dự án ASP.NET MVC khi mới bắt đầu đã chứa rất nhiều nội dung, chúng tôi không giải thích hết tất cả mà chỉ những phần cần thiết phải thay đổi để có thể sử dụng BotDetect CAPTCHA cho trang đăng ký. Chú ý rằng ví dụ mẫu này cần có BotDetect ASP.NET CAPTCHA phiên bản 2.0.13 hoặc cao hơn.

Vị trí dự án mẫu

Mặc định, dự án mẫu này được cài đặt tại
C:\Program Files\Lanapsoft\BotDetect\ASP.NET 2.0\v2.0\Samples\CSharpBotDetect2MvcDemo\.

Bạn cũng có thể chạy nó từ Start Menu:
Programs > Lanapsoft > BotDetect > ASP.NET 2.0 > v2.0 > Samples > C# BotDetect ASP.NET MVC CAPTCHA Sample.

Views/Account/Register.aspx

Mã nguồn đầy đủ

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
  Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="registerHead" ContentPlaceHolderID="head" 
  runat="server">
  <title>Register</title>
</asp:Content>

<asp:Content ID="registerContent" ContentPlaceHolderID="MainContent" 
  runat="server">
  <h2>Create a New Account</h2>
    <p>
      Use the form below to create a new account. 
    </p>
    <p>
      Passwords are required to be a minimum of <%=Html.Encode(
        ViewData["PasswordLength"])%> characters in length.
    </p>
    <%= Html.ValidationSummary() %>

    <% using (Html.BeginForm()) { %>
      <div>
        <fieldset>
          <legend>Account Information</legend>
          <p>
            <label for="username">Username:</label>
            <%= Html.TextBox("username") %>
            <%= Html.ValidationMessage("username") %>
          </p>
          <p>
            <label for="email">Email:</label>
            <%= Html.TextBox("email") %>
            <%= Html.ValidationMessage("email") %>
          </p>
          <p>
            <label for="password">Password:</label>
            <%= Html.Password("password") %>
            <%= Html.ValidationMessage("password") %>
          </p>
          <p>
            <label for="confirmPassword">Confirm password:</label>
            <%= Html.Password("confirmPassword") %>
            <%= Html.ValidationMessage("confirmPassword") %>
          </p>
          
          <% 
            Lanap.BotDetect.MvcCaptcha registrationCaptcha = 
              new Lanap.BotDetect.MvcCaptcha("RegistrationCaptcha");
              
            registrationCaptcha.TextStyle = 
              Lanap.BotDetect.TextStyleEnum.Lego;
              
            registrationCaptcha.CodeLength = 4;
            
            registrationCaptcha.ImageSize = 
              new System.Drawing.Size(245, 50);
            
            registrationCaptcha.SaveProperties();
  
            if (!registrationCaptcha.IsSolved)
            {
          %>
          
          <%= Html.Captcha(registrationCaptcha) %>
          <p>
            <label for="captchaCode">Type the characters you see 
              in the picture:</label>
            <%= Html.TextBox("captchaCode") %>
            <%= Html.ValidationMessage("captchaCode")%>
          </p>
          <script type="text/javascript">
            function LBD_ClearUserInput() {
              var LBD_textBox = document.getElementById('captchaCode');
              if(LBD_textBox) {
                LBD_textBox.value = '';
              }
            }
            LBD_RegisterHandler('PreReloadCaptchaImage', 
              LBD_ClearUserInput);
          </script>
          
          <% 
            }
          %>
          
          <p>
            <input type="submit" value="Register" />
          </p>
        </fieldset>
      </div>
    <% } %>
</asp:Content>

Giải thích

Thay vì sử dụng Lanap.BotDetect.Captcha thông thường cho ứng dụng ASP.NET, chúng ta sử dụng lớp đặc biệt Lanap.BotDetect.MvcCaptcha, và thiết lập thuộc tính của nó trực tiếp trong mã nguồn của trang. Lưu ý rằng sau khi thiết lập thích hợp, chúng ta phải gọi phương thức SaveProperties - nếu không những thiết lập này sẽ không được lưu.

Sau khi khởi động cho đối tượng MvcCaptcha, ta truyền nó như là một tham số cho HtmlHelper sử dụng cho MvcCaptcha, nhưng chỉ khi nó chưa được giải thành công. Nói cách khác, một khi người dùng trả lời đúng Captcha, anh ta đã được chứng nhận không phải là máy, và Captcha sẽ không được hiển thị cho dù các trường khác (ví dụ, địa chỉ email) không hợp lệ.

Attributes/CaptchaValidationAttribute.cs

Mã nguồn đầy đủ

using System;
using System.Web.Mvc;

namespace Lanap.BotDetect.Attributes
{
  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, 
    Inherited = false)]
  public sealed class CaptchaValidationAttribute : ActionFilterAttribute
  {
    public CaptchaValidationAttribute()
      : this("captchaCode", "RegistrationCaptcha")
    {
    }

    public CaptchaValidationAttribute(string inputfield, 
      string captchaId) 
    {
      this.Field = inputfield;
      this.CaptchaId = captchaId;
    }

    private string Field { get; set; }

    private string CaptchaId { get; set; }

    public override void OnActionExecuting(ActionExecutingContext 
      filterContext)
    {
      // make sure the captcha valid key is not contained in the 
      // route data
      if (filterContext.RouteData.Values.ContainsKey("captchaValid"))
      {
        filterContext.RouteData.Values.Remove("captchaValid");
      }

      if (string.IsNullOrEmpty(this.CaptchaId))
      {
        filterContext.RouteData.Values.Add("captchaValid", false);
        return;
      }

      string captchaCode = 
        filterContext.HttpContext.Request.Form[this.Field];

      // Captcha validation
      MvcCaptcha captchaInstance = new MvcCaptcha(this.CaptchaId);
			
      string captchaInstanceId = 
        filterContext.HttpContext.Request.Form[
          captchaInstance.CaptchaIdKey];

      // the Captcha is only validated if it was included on the page
      if (!(String.IsNullOrEmpty(captchaInstanceId)))
      {
        if ((captchaInstance.Validate(captchaCode, captchaInstanceId)))
        {
          filterContext.RouteData.Values.Add("captchaValid", true);
          return;
        }
      }
      filterContext.RouteData.Values.Add("captchaValid", false);
      return;
    }
  }
}

Giải thích

Để xác thực câu trả lời của người dùng, ta tạo mới một đối tượng MvcCaptcha với cùng tên được thêm vào trang Register.aspx ("RegistrationCaptcha"), và có thể xác thực câu trả lời của người dùng.

Bằng cách kiểm tra sự tồn tại của trường ẩn captchaInstanceId, chúng ta có thể kiểm tra Captcha có tồn tại trên trang hay không. Điều này cho phép chúng ta chỉ xác thực Captcha nếu người dùng chưa trả lời nó.

Tên dùng cho mỗi Captcha trong ứng dụng cho phép chúng ta có nhiều Captcha khác nhau và xác thực riêng biệt - ví dụ, chúng ta có thể có trang PostComment.aspx gắn liền với hàm PostComment trong một controller khác, và chúng ta có thể dùng tên Captcha khác("CommentCaptcha") để phân biệt giữa chúng.

Controllers/AccountController.cs

Mã nguồn

Vì file mã nguồn này chứa rất nhiều mã tự động sinh bởi VS, chúng tôi chỉ thể hiện phần cần thiết để sử dụng BotDetect CAPTCHA trong chức năng đăng ký.

[AcceptVerbs(HttpVerbs.Post)]
[CaptchaValidation("captchaCode", "RegistrationCaptcha")]
public ActionResult Register(string userName, string email, 
  string password, string confirmPassword, string captchaCode)
{
  ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

  if (ValidateRegistration(userName, email, password, confirmPassword, 
    captchaCode))
  {
    // Attempt to register the user
    MembershipCreateStatus createStatus = 
      MembershipService.CreateUser(userName, password, email);

    if (createStatus == MembershipCreateStatus.Success)
    {
      FormsAuth.SignIn(userName, false /* createPersistentCookie */);
      return RedirectToAction("Index", "Home");
    }
    else
    {
      ModelState.AddModelError("_FORM", 
        ErrorCodeToString(createStatus));
    }
  }

  // If we got this far, something failed, redisplay form
  return View();
}
private bool ValidateRegistration(string userName, string email, 
  string password, string confirmPassword, string captchaCode)
{
  if (String.IsNullOrEmpty(userName))
  {
    ModelState.AddModelError("username", 
      "You must specify a username.");
  }
  if (String.IsNullOrEmpty(email))
  {
    ModelState.AddModelError("email", 
      "You must specify an email address.");
  }
  if (password == null || 
      password.Length < MembershipService.MinPasswordLength)
  {
    ModelState.AddModelError("password",
      String.Format(CultureInfo.CurrentCulture,
        "You must specify a password of {0} or more characters.",
        MembershipService.MinPasswordLength));
  }
  if (!String.Equals(password, confirmPassword, 
    StringComparison.Ordinal))
  {
    ModelState.AddModelError("_FORM", 
      "The new password and confirmation password do not match.");
  }

  // Captcha validation
  if (!(bool)this.RouteData.Values["captchaValid"])
  {
    ModelState.AddModelError("captchaCode", 
      "The CAPTCHA code was incorrect!");

  }

  return ModelState.IsValid;
}

Giải thích

Vì chúng ta thêm một TextBox mới để người dùng nhập câu trả lời, chúng ta phải chỉnh sửa phương thức RegisterValidateRegistration để chấp nhận tham số string thêm vào - captchaCode. Trong phương thức ValidateRegistration, chúng ta thêm việc kiểm tra xác thực CAPTCHA

Views/Shared/Site.Master

Mã nguồn đầy đủ

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <meta http-equiv="Content-Type" 
    content="text/html; charset=iso-8859-1" />
  <asp:ContentPlaceHolder ID="head" runat="server">
    <title></title>
  </asp:ContentPlaceHolder>
  <asp:PlaceHolder runat="server" id="includes">
	
    <link href="<%= VirtualPathUtility.ToAbsolute(
      "~/Content/Site.css") %>" rel="stylesheet" type="text/css" />
			
    <link href="<%= VirtualPathUtility.ToAbsolute(
      "~/Content/BotDetectLayout.css") %>" rel="stylesheet" 
      type="text/css" />
			
    <script type='text/javascript' src='<%= VirtualPathUtility.
      ToAbsolute("~/Content/BotDetectScripts.js") %>'></script>
			
  </asp:PlaceHolder>
</head>

<body>
  <div class="page">

    <div id="header">
      <div id="title">
        <h1>My MVC Application</h1>
      </div>
        
      <div id="logindisplay">
        <% Html.RenderPartial("LogOnUserControl"); %>
      </div> 
      
      <div id="menucontainer">
      
        <ul id="menu">              
          <li><%= Html.ActionLink("Home", "Index", "Home")%></li>
          <li><%= Html.ActionLink("About", "About", "Home")%></li>
        </ul>
      
      </div>
    </div>

    <div id="main">
      <asp:ContentPlaceHolder ID="MainContent" runat="server" />

      <div id="footer">
      </div>
    </div>
  </div>
</body>
</html>

Giải thích

Thay đổi duy nhất trên trang Master là thêm scripts BotDetect phía máy khách và khai báo CSS, được copy vào thư mục Content. Tất cả các includes được thay đổi sử dụng đường dẫn tuyệt đối (để mọi trang ở mọi mức có thể sử dụng chúng), và đặt với một PlaceHolder để tránh lỗi tiêu đề trong ASP.NET MVC.

Views/Shared/HtmlHelper.cs

Mã nguồn đầy đủ

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Text;

namespace System.Web.Mvc
{
  public static class CaptchaHtmlHelper
  {
    public static string Captcha(this HtmlHelper helper, 
      object captchaInstance)
    {
      Lanap.BotDetect.MvcCaptcha captcha = 
        captchaInstance as Lanap.BotDetect.MvcCaptcha;
        
      StringBuilder markupBuilder = new StringBuilder();
      
      markupBuilder.Append(@"<div class='LBD_CaptchaDiv' style='width:");

      markupBuilder.Append(captcha.ImageSize.Width + 28);
      
      markupBuilder.Append(@"px; height: ");
      
      markupBuilder.Append(captcha.ImageSize.Height + 10);
      
      markupBuilder.Append(@"px;'>
<div class='LBD_CaptchaImage' style='width:");

      markupBuilder.Append(captcha.ImageSize.Width);
      
      markupBuilder.Append(@"px; height: ");
      
      markupBuilder.Append(captcha.ImageSize.Height);
      
      markupBuilder.Append(@"px; '>
  <img id='");
  
      markupBuilder.Append(captcha.CaptchaId);
      
      markupBuilder.Append(@"_CaptchaImage' src='");
      
      markupBuilder.Append(captcha.ImageLink);
      
      markupBuilder.Append(@"' alt='CAPTCHA Code Image' />
</div>
<div class='LBD_CaptchaIcons'>
  <a href="");
  
      markupBuilder.Append(captcha.SoundLink);
      
      markupBuilder.Append("' onclick='LBD_LoadSound(\"");
      
      markupBuilder.Append(captcha.CaptchaId);
      
      markupBuilder.Append("_SoundPlaceholder\", \"");
      
      markupBuilder.Append(captcha.SoundLink);
      
      markupBuilder.AppendFormat("\");this.blur();return false;'>
        <img src='{0}' alt='Play Sound' /></a>", 
        VirtualPathUtility.ToAbsolute("~/Content/Speaker.gif"));
      
      markupBuilder.Append("<a href="#" onclick='LBD_ReloadImage(\"");
      
      markupBuilder.Append(captcha.CaptchaId);
      
      markupBuilder.AppendFormat("_CaptchaImage\");this.blur();
        return false;'><img src='{0}' alt='Change the code' />
        </a>", VirtualPathUtility.ToAbsolute("~/Content/Refresh.gif"));
      
      markupBuilder.Append("<div id='");
      
      markupBuilder.Append(captcha.CaptchaId);
      
      markupBuilder.Append(@"_SoundPlaceholder' 
        class='LBD_placeholder'> </div>
</div>
</div>

<input type='hidden' 
id='");

      markupBuilder.Append(captcha.CaptchaIdKey);
      
      markupBuilder.Append(@"' 
name='");

      markupBuilder.Append(captcha.CaptchaIdKey);
      
      markupBuilder.Append(@"' 
value='");

      markupBuilder.Append(captcha.CurrentInstanceId); 
      
      markupBuilder.Append(@"' />");

      return markupBuilder.ToString();
    }
  }
}

Giải thích

Trong file này, chúng ta sử dụng phương thức mở rộng để cho phép MvcCaptcha hiển thị từ tất cả các trang ASP.NET MVC. Biểu tượng speaker và reload được tải từ thư mục Content của ứng dụng.

Content/BotDetectLayout.css

Mã nguồn đầy đủ

.LBD_CaptchaDiv {
  padding: 0 !important;
  margin: 20px 0 0 10px;
  overflow: visible !important;
}

*html .LBD_CaptchaDiv {
  margin-bottom: -3px !important;
  overflow: hidden !important;
}

.LBD_CaptchaDiv .LBD_CaptchaImage
{
  float: left !important;
  margin: 0 !important;
  padding: 0 !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons 
{
  width: 22px !important;
  float: right !important;
  text-align: left !important;
  margin: 0 !important;
  padding: 0 !important;
  margin-bottom: -4px !important;
  margin-top: -1px !important;
  margin-right: 2px !important;
}

*html .LBD_CaptchaDiv .LBD_CaptchaIcons 
{
  margin-right: 1px !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons a
{
  margin: 0 !important;
  padding: 0 !important;
  display: block !important;
  background-color: transparent !important;
  text-decoration: none !important;
  border: none !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons a:focus, 
.LBD_CaptchaDiv .LBD_CaptchaIcons a:active
{
  border: none !important;
  -moz-outline:none !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons a img
{
  border: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  margin-top: 2px !important;
  margin-bottom: 3px !important;
  display: block !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons a:focus img, 
.LBD_CaptchaDiv .LBD_CaptchaIcons a:active img
{
  border: 1px dotted #333 !important;
  margin: 0 !important;
  padding: 0 !important;
  margin-top: 1px !important;
  margin-bottom: 1px !important;
  display: block !important;
}

*html .LBD_CaptchaDiv .LBD_CaptchaIcons a:focus img, 
*html .LBD_CaptchaDiv .LBD_CaptchaIcons a:active img
{
  border: 1px solid #999 !important;
  margin-bottom: 3px !important;
}

*:first-child+html .LBD_CaptchaDiv .LBD_CaptchaIcons a:focus img, 
*:first-child+html .LBD_CaptchaDiv .LBD_CaptchaIcons a:active img
{
  border: 1px solid #999 !important;
}

.LBD_CaptchaDiv .LBD_CaptchaIcons .LBD_placeholder
{
  visibility: hidden !important; 
  width: 0 !important; 
  height: 0 !important;
}

*html .LBD_CaptchaDiv .LBD_CaptchaIcons .LBD_placeholder
{
  display: none !important; 
}

*:first-child+html .LBD_CaptchaDiv .LBD_CaptchaIcons .LBD_placeholder
{
  display: none !important; 
}

Giải thích

In Web Forms ASP.NET applications, the CSS declarations used by the BotDetect Captcha control are embedded in the Lanap.BotDetect.dll assembly and included as Web Resources. Since this approach is not available in ASP.NET MVC applications, the same CSS declarations are here included as a plain .css file.

Content/BotDetectScripts.js

Mã nguồn

var LBD_ImgId = null;
var LBD_Img = null;
var LBD_NewImg = null; 
var LBD_Parent = null;
var LBD_Prompt = null;
var LBD_Timer = null;
var LBD_TimerTicks = 0;

function LBD_LoadSound(soundPlaceholderId, soundLink)
{
  if(document.getElementById)
  {
    var i = soundLink.indexOf('&d=');
    if (-1 != i) {
      soundLink = soundLink.substring(0, i);
    }
    
    soundLink = soundLink + '&d=' + LBD_GetTimestamp();
    
    if ((-1 == soundLink.indexOf('&e=')) && 
        (document.location.protocol == "https:")) {
      soundLink = soundLink + '&e=1';
    }

    var placeholder = document.getElementById(soundPlaceholderId);
    var objectSrc = "&lt;object id='captchaSound' 
      classid='clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95' 
      height='0' width='0' style='width:0; height:0;'&gt;
      &lt;param name='AutoStart' value='1' /&gt;
      &lt;param name='Volume' value='0' /&gt;
      &lt;param name='PlayCount' value='1' /&gt;
      &lt;param name='FileName' value='" + soundLink + "' /&gt;
      &lt;embed id='captchaSoundEmbed' src='"+ soundLink + 
        "' autoplay='true' hidden='true' volume='100' 
        type='"+ LBD_GetMimeType() +"' style='display:inline;' /&gt;
    &lt;/object&gt;";

    placeholder.innerHTML = "";
    placeholder.innerHTML = objectSrc;
  }
}

function LBD_GetTimestamp() {
  var d = new Date();
  var t = d.getTime() + (d.getTimezoneOffset() * 60000);
  return t;
}

function LBD_GetMimeType()
{
  var mimeType = "audio/x-wav";
  return mimeType;
}

function LBD_ReloadImage(imgId)
{ 
  if(imgId)
  {
    LBD_ImgId = imgId;
    LBD_Img = document.getElementById(LBD_ImgId);
    var src = LBD_Img.src;
    
    var i = src.indexOf('&d=');
    if (-1 != i) {
      src = src.substring(0, i);
    }
    var newSrc = src + '&d=' + LBD_GetTimestamp();

    LBD_NewImg = document.createElement('img');
    LBD_NewImg.onload = LBD_ShowImage;
    LBD_NewImg.id = LBD_Img.id;
    LBD_NewImg.alt = LBD_Img.alt;
    LBD_NewImg.src = newSrc;

    LBD_Prompt = document.createElement('span');
    LBD_Prompt.appendChild(document.createTextNode('.'));

    LBD_PreReloadImage();
    LBD_Parent = LBD_Img.parentNode;
    LBD_Parent.removeChild(LBD_Img);
    LBD_Parent.appendChild(LBD_Prompt);

    LBD_ShowProgress()
  }
}	

function LBD_ShowProgress()
{
  if((LBD_Prompt)&&(LBD_TimerTicks < 100))
  {
    LBD_TimerTicks = LBD_TimerTicks + 1;

    if(0 == LBD_TimerTicks % 5)
    {
      LBD_Prompt.firstChild.nodeValue = '.';     
    }
    else
    {
      LBD_Prompt.firstChild.nodeValue = 
        LBD_Prompt.firstChild.nodeValue + '.';
    }

    LBD_Timer = setTimeout("LBD_ShowProgress()", 250);
  }
  else
  {
    clearTimeout(LBD_Timer);
    LBD_TimerTicks = 0;
  }
}

function LBD_ShowImage()
{
  if(LBD_NewImg && LBD_Parent && LBD_Prompt)
  {
    LBD_Parent.removeChild(LBD_Prompt);
    LBD_Parent.appendChild(LBD_NewImg);
    LBD_Prompt = null;
    LBD_PostReloadImage();
  }
}

function LBD_PreReloadImage() {}
function LBD_PostReloadImage() {}

function LBD_RegisterHandler(eventName, userHandler)
{
  switch(eventName.toLowerCase())
  {
    case 'prereloadcaptchaimage':
      var LBD_OldHandler = LBD_PreReloadImage;
      LBD_PreReloadImage = function() {
        LBD_OldHandler();
        userHandler();
      }
      break;
    case 'postreloadcaptchaimage':
      var oldHandler = LBD_PostReloadImage;
      LBD_PostReloadImage = function() {
        LBD_OldHandler();
        userHandler();
      }
      break;
  }
}

if ((typeof(Sys) != "undefined") && 
    (typeof(Sys.Application) != "undefined")) {
  Sys.Application.notifyScriptLoaded();
}

Giải thích

Trong ứng dụng ASP.NET, khai báo CSS được sử dụng bởi BotDetect Captcha được nhúng trong Lanap.BotDetect.dll như là Web Resources. Vì cách này không thể sử dụng trong ứng dụng ASP.NET MVC, khai báo CSS tương tự được đặt trong file .css.

Content/BotDetectScripts.js

Mã nguồn đầy đủ

var LBD_ImgId = null;
var LBD_Img = null;
var LBD_NewImg = null; 
var LBD_Parent = null;
var LBD_Prompt = null;

function LBD_LoadSound(soundPlaceholderId, soundLink)
{
  if(document.getElementById)
  {
    var i = soundLink.indexOf('&d=');
    if (-1 != i) {
      soundLink = soundLink.substring(0, i);
    }
    
    soundLink = soundLink + '&d=' + LBD_GetTimestamp();
    
    if ((-1 == soundLink.indexOf('&e=')) && 
        (document.location.protocol == "https:")) {
      soundLink = soundLink + '&e=1';
    }

    var placeholder = document.getElementById(soundPlaceholderId);
    var objectSrc = "&lt;object id='captchaSound' 
      classid='clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95' 
      height='0' width='0' style='width:0; height:0;'&gt;
      &lt;param name='AutoStart' value='1' /&gt;
      &lt;param name='Volume' value='0' /&gt;
      &lt;param name='PlayCount' value='1' /&gt;
      &lt;param name='FileName' value='" + soundLink + "' /&gt;
      &lt;embed id='captchaSoundEmbed' src='"+ soundLink + 
        "' autoplay='true' hidden='true' volume='100' 
        type='"+ LBD_GetMimeType() +"' style='display:inline;' /&gt;
    &lt;/object&gt;";

    placeholder.innerHTML = "";
    placeholder.innerHTML = objectSrc;
  }
}

function LBD_GetTimestamp() {
  var d = new Date();
  var t = d.getTime() + (d.getTimezoneOffset() * 60000);
  return t;
}

function LBD_GetMimeType()
{
  var mimeType = "audio/x-wav";
  return mimeType;
}

var LBD_ImgId = null;
var LBD_Img = null;
var LBD_NewImg = null; 
var LBD_Parent = null;
var LBD_Prompt = null;

function LBD_ReloadImage(imgId)
{  
  if(imgId)
  {
    LBD_ImgId = imgId;
    LBD_Img = document.getElementById(LBD_ImgId);
    var src = LBD_Img.src;
    
    var i = src.indexOf('&d=');
    if (-1 != i) {
      src = src.substring(0, i);
    }
    var newSrc = src + '&d=' + LBD_GetTimestamp();

    LBD_NewImg = document.createElement('img');
    LBD_NewImg.onload = LBD_ShowImage;
    LBD_NewImg.id = LBD_Img.id;
    LBD_NewImg.alt = LBD_Img.alt;
    LBD_NewImg.src = newSrc;

    LBD_Prompt = document.createElement('span');
    LBD_Prompt.appendChild(document.createTextNode('loading...'));

    LBD_Parent = LBD_Img.parentNode;
    LBD_Parent.removeChild(LBD_Img);
    LBD_Parent.appendChild(LBD_Prompt);
  }
}

function LBD_ShowImage()
{
  if(LBD_NewImg && LBD_Parent && LBD_Prompt)
  {
    LBD_Parent.removeChild(LBD_Prompt);
    LBD_Parent.appendChild(LBD_NewImg);
  }
}

if ((typeof(Sys) != "undefined") && 
    (typeof(Sys.Application) != "undefined")) {
  Sys.Application.notifyScriptLoaded();
}

Giải thích

Trong ứng dụng ASP.NET, JavaScript ở phía máy khách sử dụng bởi BotDetect Captcha được nhúng trong Lanap.BotDetect.dll như là Web Resources. Vì cách này không sử dụng được trong ứng dụng ASP.NET MVC, hàm JavaScript tương tự được đặt trong file .js.

Global.asax.cs

Mã nguồn đầy đủ

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace CSharpBotDetectMvcDemo
{
  // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
  // visit http://go.microsoft.com/?LinkId=9394801

  public class MvcApplication : System.Web.HttpApplication
  {
    public static void RegisterRoutes(RouteCollection routes)
    {
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

      routes.MapRoute(
        "Default", 
        "{controller}/{action}/{id}",
        new { controller = "Account", action = "Register", id = "" }
      );

      routes.MapRoute(
        "Root",
        "",
        new { controller = "Account", action = "Register", id = "" }
      );
    }

    protected void Application_Start()
    {
      RegisterRoutes(RouteTable.Routes);
    }
  }
}

Giải thích

Để cho hàm Register trở thành hàm mặc định trong ứng dụng (mục đích là để CAPTCHA hiển thị ngay tức thì khi khởi động ứng dụng), chúng ta thay đổi phương thức RegisterRoutes.

Web.config

Mã nguồn đầy đủ

<?xml version="1.0"?>
<configuration>

  <configSections>
    <sectionGroup name="system.web.extensions" type="
      System.Web.Configuration.SystemWebExtensionsSectionGroup, 
      System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
      PublicKeyToken=31BF3856AD364E35">
      <sectionGroup name="scripting" type="System.Web.
        Configuration.ScriptingSectionGroup, System.Web.Extensions, 
        Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35">
        <section name="scriptResourceHandler" type="System.Web.
          Configuration.ScriptingScriptResourceHandlerSection, 
          System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31BF3856AD364E35" requirePermission="false"
          allowDefinition="MachineToApplication"/>
        <sectionGroup name="webServices" type="System.Web.
          Configuration.ScriptingWebServicesSectionGroup, 
          System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31BF3856AD364E35">
          <section name="jsonSerialization" type="System.Web.
            Configuration.ScriptingJsonSerializationSection, 
            System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
            PublicKeyToken=31BF3856AD364E35" requirePermission="false" 
            allowDefinition="Everywhere" />
          <section name="profileService" type="System.Web.
            Configuration.ScriptingProfileServiceSection, 
            System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
            PublicKeyToken=31BF3856AD364E35" requirePermission="false" 
            allowDefinition="MachineToApplication" />
          <section name="authenticationService" type="System.Web.
            Configuration.ScriptingAuthenticationServiceSection, 
            System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
            PublicKeyToken=31BF3856AD364E35" requirePermission="false" 
            allowDefinition="MachineToApplication" />
          <section name="roleService" type="System.Web.Configuration.
            ScriptingRoleServiceSection, System.Web.Extensions, 
            Version=3.5.0.0, Culture=neutral, 
            PublicKeyToken=31BF3856AD364E35" requirePermission="false" 
            allowDefinition="MachineToApplication" />
        </sectionGroup>
      </sectionGroup>
    </sectionGroup>
  </configSections>

  <appSettings>
    <add key="LBD_RequestPath" value="LanapCaptcha.axd" />
  </appSettings>

  <connectionStrings>
    <add name="ApplicationServices" connectionString="
      data source=.\SQLEXPRESS;Integrated Security=SSPI;
      AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" 
      providerName="System.Data.SqlClient"/>
  </connectionStrings>

  <system.web>
    <compilation debug="false">
      <assemblies>
        <add assembly="System.Core, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Web.Extensions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Abstractions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Routing, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Mvc, Version=1.0.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Xml.Linq, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Data.Linq, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=B77A5C561934E089" />
      </assemblies>
    </compilation>

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" />
    </authentication>

    <membership>
      <providers>
        <clear/>
        <add name="AspNetSqlMembershipProvider"
          type="System.Web.Security.SqlMembershipProvider, 
            System.Web, Version=2.0.0.0, Culture=neutral, 
            PublicKeyToken=b03f5f7f11d50a3a"
          connectionStringName="ApplicationServices"
          enablePasswordRetrieval="false"
          enablePasswordReset="true"
          requiresQuestionAndAnswer="false"
          requiresUniqueEmail="false"
          passwordFormat="Hashed"
          maxInvalidPasswordAttempts="5"
          minRequiredPasswordLength="6"
          minRequiredNonalphanumericCharacters="0"
          passwordAttemptWindow="10"
          passwordStrengthRegularExpression=""
          applicationName="/"
        />
      </providers>
    </membership>

    <profile>
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider"
          type="System.Web.Profile.SqlProfileProvider, System.Web, 
            Version=2.0.0.0, Culture=neutral, 
            PublicKeyToken=b03f5f7f11d50a3a"
          connectionStringName="ApplicationServices"
          applicationName="/"
        />
      </providers>
    </profile>

    <roleManager enabled="false">
      <providers>
        <clear />
        <add connectionStringName="ApplicationServices" 
          applicationName="/" name="AspNetSqlRoleProvider" 
          type="System.Web.Security.SqlRoleProvider, System.Web, 
            Version=2.0.0.0, Culture=neutral, 
            PublicKeyToken=b03f5f7f11d50a3a" />
        <add applicationName="/" name="AspNetWindowsTokenRoleProvider" 
          type="System.Web.Security.WindowsTokenRoleProvider, 
            System.Web, Version=2.0.0.0, Culture=neutral, 
            PublicKeyToken=b03f5f7f11d50a3a" />
      </providers>
    </roleManager>

    <pages>
      <controls>
        <add tagPrefix="asp" namespace="System.Web.UI" 
          assembly="System.Web.Extensions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add tagPrefix="asp" namespace="System.Web.UI.WebControls" 
          assembly="System.Web.Extensions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      </controls>

      <namespaces>
        <add namespace="System.Web.Mvc"/>
        <add namespace="System.Web.Mvc.Ajax"/>
        <add namespace="System.Web.Mvc.Html"/>
        <add namespace="System.Web.Routing"/>
        <add namespace="System.Linq"/>
        <add namespace="System.Collections.Generic"/>
      </namespaces>
    </pages>

    <sessionState mode="InProc" cookieless="AutoDetect" timeout="20"
      sessionIDManagerType="Lanap.BotDetect.Persistence.
      CustomSessionIDManager, Lanap.BotDetect" />

    <customErrors mode="On" defaultRedirect="~/Content/Error.html"/>

    <httpHandlers>
      <remove verb="*" path="*.asmx"/>
      <add verb="*" path="LanapCaptcha.axd" 
        type="Lanap.BotDetect.CaptchaHandler, Lanap.BotDetect" />
      <add verb="*" path="*.asmx" validate="false" 
        type="System.Web.Script.Services.ScriptHandlerFactory, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add verb="*" path="*_AppService.axd" validate="false" 
        type="System.Web.Script.Services.ScriptHandlerFactory, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add verb="GET,HEAD" path="ScriptResource.axd" 
        type="System.Web.Handlers.ScriptResourceHandler, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35" validate="false"/>
      <add verb="*" path="*.mvc" validate="false" 
        type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, 
        Version=1.0.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
    </httpHandlers>

    <httpModules>
      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingModule" type="System.Web.Routing.
        UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, 
        Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </httpModules>

  </system.web>

  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" 
        warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, 
          System, Version=2.0.0.0, Culture=neutral, 
          PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5"/>
        <providerOption name="WarnAsError" value="false"/>
      </compiler>

      <compiler language="vb;vbs;visualbasic;vbscript" 
        extension=".vb" warningLevel="4" type="Microsoft.VisualBasic.
          VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, 
          PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5"/>
        <providerOption name="OptionInfer" value="true"/>
        <providerOption name="WarnAsError" value="false"/>
      </compiler>
    </compilers>
  </system.codedom>

  <system.web.extensions/>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>

    <modules runAllManagedModulesForAllRequests="true">
      <remove name="ScriptModule" />
      <remove name="UrlRoutingModule" />
      <add name="ScriptModule" preCondition="managedHandler" 
        type="System.Web.Handlers.ScriptModule, System.Web.Extensions, 
        Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingModule" type="System.Web.Routing.
        UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, 
        Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </modules>

    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated"/>
      <remove name="ScriptHandlerFactory" />
      <remove name="ScriptHandlerFactoryAppServices" />
      <remove name="ScriptResource" />
      <remove name="MvcHttpHandler" />
      <remove name="UrlRoutingHandler" />
      <remove name="LanapCaptchaHandler" />
      <add name="ScriptHandlerFactory" verb="*" path="*.asmx" 
        preCondition="integratedMode" type="System.Web.Script.
          Services.ScriptHandlerFactory, System.Web.Extensions,
          Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptHandlerFactoryAppServices" verb="*" 
        path="*_AppService.axd" preCondition="integratedMode"
        type="System.Web.Script.Services.ScriptHandlerFactory, 
          System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
          PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptResource" preCondition="integratedMode" 
        verb="GET,HEAD" path="ScriptResource.axd" 
        type="System.Web.Handlers.ScriptResourceHandler, 
        System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35" />
      <add name="MvcHttpHandler" preCondition="integratedMode" 
        verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, 
        System.Web.Mvc, Version=1.0.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingHandler" preCondition="integratedMode" 
        verb="*" path="UrlRouting.axd" type="System.Web.
          HttpForbiddenHandler, System.Web, Version=2.0.0.0, 
          Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
      <add name="LanapCaptchaHandler" verb="*" 
        path="LanapCaptcha.axd" type="Lanap.BotDetect.CaptchaHandler, 
        Lanap.BotDetect" />

    </handlers>
  </system.webServer>

</configuration>

Giải thích

Thành tố đầu tiên chúng tao phải thêm vào file Web.config là khai báo LBD_RequestPath trong phần <appSettings>. Thành tố này được sử dụng để cấu hình đường dẫn BotDetect CAPTCHA để sử dụng đuôi .axd, vì mặc định trình thực thi ASP.NET MVC xử lý đuôi .aspx.

Thay đổi tiếp theo là khai báo <sessionState>, phải đăng ký một sessionIDManager, đảm bảo âm thanh CAPTCHA chạy tốt trên Google Chrome và IE 7.0 + Vista.

Phần <customErrors> được sử dụng để hiển thị trang trỏ tới trang tải về, vì không cài đặt ASP.NET MVC là lỗi thường xảy ra khi không dùng được ví dụ này. Nếu bạn đã cài đặt ASP.NET MVC và vẫn bị dẫn tới trang Error.html, hãy bỏ phần <customErrors> để có thể nhìn thấy chi tiết lỗi.

Cuối cùng, chúng ta đăng ký BotDetect HttpHandler sử dụng cho hình ảnh CAPTCHA và sinh âm thanh trong cả hai <httpHandlers> và phần <system.webServer>/<handlers> (để CAPTCHA hoạt động tốt cho cả IIS 7.0 và các phiên bản trước). Chúng ta sử dụng đường dẫn .axd trong khai báo handler, như giải thích ở trên.

language: English Español Tiếng Việt