Mã nguồn ví dụ mẫu BotDetect ASP.NET 2.0 CAPTCHA - C#

Ví dụ mẫu sinh ngẫu nhiên BotDetect ASP.NET CAPTCHA cho bạn thấy việc sinh ngẫu nhiên các tham số của Captcha dễ dàng như thế nào, việc này làm tăng đáng kể tính bảo mật của CAPTCHA và đây là cách tốt nhất để tận dụng hết 50 giải thuật khác nhau của BotDetect.

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\CSharpBotDetect2RandomDemo\.

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

Default.aspx

Mã nguồn đầy đủ

<%@ Page Language="C#" AutoEventWireup="true" 
  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ Register Assembly="Lanap.BotDetect" Namespace="Lanap.BotDetect" 
  TagPrefix="BotDetect" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>BotDetect Random Demo</title>
    <link type='text/css' rel='Stylesheet' href="StyleSheet.css" />
</head>
<body>
    <form id="form1" runat="server">
    <fieldset id="Preview">
        <legend>
            <span id="PreviewLegend">CAPTCHA Preview</span>
        </legend>
        <div id="PromptDiv">
            <span id="Prompt">Type the characters you see in 
              the picture</span>
        </div>
        <div id="CaptchaDiv">
            <BotDetect:Captcha ID="SampleCaptcha" runat="server" />
        </div>
        <div id="ValidationDiv">
            <asp:TextBox ID="CodeTextBox" runat="server">
            </asp:TextBox>
            <asp:Button ID="ValidateButton" runat="server" />
            <asp:Label ID="MessageCorrectLabel" runat="server">
            </asp:Label>
            <asp:Label ID="MessageIncorrectLabel" runat="server">
            </asp:Label>
        </div>
    </fieldset>
    <div id="Note">
        <span>NOTE: the Trial version will use "LANAP" instead of a 
          random code in 50% of renderings.</span>
    </div>
    </form>
</body>
</html>

Giải thích

Những dòng cần thiết để thêm BotDetect CAPTCHA vào trang ASP.NET được tô đậm. Để sử dụng <BotDetect:Captcha>, trước hết chúng ta phải đăng ký Lanap.BotDetect.dll sử dụng <%@Register %>.

Trang web còn chứa <asp:TextBox> để người dùng nhập câu trả lời, một <asp:Button> để gửi câu trả lời, và một cặp <asp:Label> dùng để hiển thị kết quả xác thực. Phần còn lại của file được sinh ra bởi Visual Studio 2005, hoặc dùng để định nghĩa cách thức sắp xếp và hiển thị của trang.

Không có sự khác biệt nào giữa file .aspx của ví dụ này với file cơ bản, vì tất cả việc sinh ngẫu nhiên đều được thực hiện ở mã xử lý trang.

Default.aspx.cs

Mã nguồn đầy đủ

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

using System.Drawing;
using Lanap.BotDetect;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Init(object sender, EventArgs e)
    {
        /// register CAPTCHA-specific event handler
        SampleCaptcha.PreDrawCaptchaImage += 
            new PreDrawCaptchaImageHandler(
                SampleCaptcha_PreDrawCaptchaImage);
    }

    protected void Page_PreRender(object sender, EventArgs e)
    {
        /// initial page setup
        if (!IsPostBack)
        {
            /// set control text
            ValidateButton.Text = "Validate";
            MessageCorrectLabel.Text = "Correct!";
            MessageIncorrectLabel.Text = "Incorrect!";

            /// these messages are shown only after validation
            MessageCorrectLabel.Visible = false;
            MessageIncorrectLabel.Visible = false;
        }

		        // clear user input on Reload button clicks
        string scriptTemplate = @"
          function LBD_ClearUserInput() {{
            var LBD_textBox = document.getElementById('{0}');
            if(LBD_textBox) {{
              LBD_textBox.value = '';
            }}
          }}
          LBD_RegisterHandler('PreReloadCaptchaImage', 
            LBD_ClearUserInput);
        ";
        string script = String.Format(scriptTemplate, 
          CodeTextBox.ClientID);
					
        if (!Page.ClientScript.IsStartupScriptRegistered(
            "CaptchaReloadClearInput"))
        {
          Page.ClientScript.RegisterStartupScript(this.GetType(), 
            "CaptchaReloadClearInput", script, true);
        }

        // automatically lowercase user input
        CodeTextBox.Attributes.Add("onkeyup", 
            "this.value = this.value.toLowerCase();");

        if (IsPostBack)
        {
            /// validate the input code, and show the 
            /// appropriate message 
            string code = CodeTextBox.Text.Trim().ToUpper();
            if (SampleCaptcha.Validate(code))
            {
                MessageCorrectLabel.Visible = true;
                MessageIncorrectLabel.Visible = false;
            }
            else
            {
                MessageCorrectLabel.Visible = false;
                MessageIncorrectLabel.Visible = true;
            }

            /// clear previous user code input
            CodeTextBox.Text = null;
        }
    }

    /// <summary>
    /// all CAPTCHA randomization should be performed in this event 
    /// handler instead of Page_Load or Page_PreRender, because this 
    /// event is also fired for direct CAPTCHA image requests (which 
    /// skip the page events since the page is never loaded), for 
    /// example when clicking the Reload CAPTCHA button repeatedly
    /// </summary>
    void SampleCaptcha_PreDrawCaptchaImage(object sender, EventArgs e)
    {
        ICaptcha captcha = sender as ICaptcha;
        if (captcha.CaptchaId != SampleCaptcha.CaptchaId)
        {
          return;
        }

        /// randomize code generation properties
        captcha.CodeType = RandomizationHelper.GetRandomCodeType();
				
        captcha.CodeLength = 
            RandomizationHelper.GetRandomCodeLength(4, 6);

        /// randomize text style
        TextStyleEnum[] styles = { 
            TextStyleEnum.Lego, TextStyleEnum.MeltingHeat, 
            TextStyleEnum.Ghostly, TextStyleEnum.FingerPrints, 
            TextStyleEnum.Graffiti2, TextStyleEnum.Bullets2, 
            TextStyleEnum.CaughtInTheNet2, TextStyleEnum.Collage, 
            TextStyleEnum.Chalkboard
        };
        
        captcha.TextStyle = 
            RandomizationHelper.GetRandomTextStyle(styles);
    }
}

Giải thích

Trong quá trình Page_Init của trang ASP.NET, chúng ta đăng ký một phương thức xử lý sự kiện đặc biệt được thực thi trước mỗi lần hiển thị hình ảnh CAPTCHA, trong phương thức này chúng ta thực hiện việc sinh ngẫu nhiên. Vì các hình ảnh CAPTCHA được sinh ra và gửi tới máy khách trong một yêu cầu Http tách biệt khỏi yêu cầu tải trang ASP.NET (khi mã này được thực thi), chúng ta thêm phương thức xử lý sự kiện để đảm bảo các tham số của CAPTCHA được sinh ngẫu nhiên mỗi khi nó được vẽ, và không chỉ một lần khi trang được tải.

Điều này là quan trọng vì số lần yêu cầu hình ảnh CAPTCHA không nhất thiết phải bằng số lần tải trang - dễ thấy nhất là khi sử dụng nút Reload CAPTCHA, và khi bots truy cập trực tiếp vào hình ảnh CAPTCHA, hình ảnh CAPTCHA có thể được sinh ra chỉ sau một lần tải của trang (và thực thi hàm Page_PreRender tương ứng).

Trong phương thức Captcha_PreDrawCaptchaImage, đối tượng Captcha được truyền qua tham số sender. Để việc sinh ngẫu nhiên được thực hiện dễ dàng, chúng ta sử dụng lớp RandomizationHelper, cho phép chúng ta lấy giá trị ngẫu nhiên của một tham số cho trước từ một tập hợp các giá trị cho trước (ví dụ như CAPTCHA CodeType), hoặc một khoảng các giá trị (ví dụ như CAPTCHA CodeLength), hay từ một tập các giá trị (như CAPTCHA TextStyle).

Bạn cũng có thể sinh ngẫu nhiên các thông số khác của CAPTCHA với cách tương tự, nhưng giải thuật vẽ CAPTCHA và chiều dài ký tự CAPTCHA sẽ tăng tính bảo mật của CAPTCHA nhiều nhất khi chúng được sinh ngẫu nhiên. Mỗi giải thuật CAPTCHA riêng rẽ có thể bị bẻ gãy (nếu dành đủ công sức), nhưng nếu bot còn phải phân biệt từng giải thuật trong từng ảnh, nhiệm vụ sẽ khó khăn hơn gấp bội. Hơn nữa, nhiều giải thuật CAPTCHA phổ biến đã bị bẻ gãy vì nó dùng chiều dài ký tự cố định – "tìm 5 ký tự trong hình này" thì dễ hơn rất nhiều so với "tìm một số ký tự chưa biết trong hình này".

App_Code\RandomizationHelper.cs

Mã nguồn đầy đủ

using System;
using System.Drawing;
using System.Collections;
using System.Collections.Specialized;
using System.Text;

using Lanap.BotDetect;

/// <summary>
/// Summary description for RandomizationHelper
/// </summary>
public sealed class RandomizationHelper
{
    private RandomizationHelper()
    {
        // constructor omitted, static methods only
    }

    /// a single global generator is used for all random numbers
    private static readonly Random _rand = new Random();

    public const CodeTypeEnum DefaultCodeType = 
        CodeTypeEnum.AlphaNumeric;

    public static CodeTypeEnum GetRandomCodeType(
        params CodeTypeEnum[] usedValues)
    {
        CodeTypeEnum codeType = DefaultCodeType;

        if (0 == usedValues.Length)
        {
            Array values = Enum.GetValues(typeof(CodeTypeEnum));
            int max = values.Length;

            codeType = (CodeTypeEnum)(_rand.Next(max));
        }
        else if (1 == usedValues.Length)
        {
            codeType = usedValues[0];
        }
        else
        {
            int max = usedValues.Length;
            int index = _rand.Next(max);

            codeType = usedValues[index];
        }

        return codeType;
    }

    public const int DefaultCodeLength = 5;
    public const int MinCodeLength = 1;
    public const int MaxCodeLength = 15;

    public static int GetRandomCodeLength()
    {
        return GetRandomCodeLength(0, 0);
    }

    public static int GetRandomCodeLength(int max)
    {
        return GetRandomCodeLength(0, max);
    }

    public static int GetRandomCodeLength(int min, int max)
    {
        if ((max > MaxCodeLength) || (max < MinCodeLength))
        {
            max = MaxCodeLength;
        }

        if ((min < MinCodeLength) || (min > max))
        {
            min = MinCodeLength;
        }

        return _rand.Next(min, max + 1);
    }

    public static readonly ImageFormatEnum DefaultImageFormat = 
        ImageFormatEnum.Jpeg;

    public static ImageFormatEnum GetRandomImageFormat(
        params ImageFormatEnum[] usedValues)
    {
        ImageFormatEnum imageFormat = DefaultImageFormat;

        if (0 == usedValues.Length)
        {
            int max = Enum.GetValues(typeof(ImageFormatEnum)).Length;

            imageFormat = (ImageFormatEnum)(_rand.Next(max));
        }
        else if (1 == usedValues.Length)
        {
            imageFormat = usedValues[0];
        }
        else
        {
            int max = usedValues.Length;
            int index = _rand.Next(max);

            imageFormat = usedValues[index];
        }

        return imageFormat;
    }

    public static readonly Size DefaultImageSize = new Size(250, 50);
    public static readonly Size MinImageSize = new Size(50, 40);
    public static readonly Size MaxImageSize = new Size(500, 200);

    public static Size GetRandomImageSize()
    {
        return GetRandomImageSize(new Size(0, 0), new Size(0, 0));
    }

    public static Size GetRandomImageSize(Size maxSize)
    {
        return GetRandomImageSize(new Size(0, 0), maxSize);
    }

    public static Size GetRandomImageSize(Size minSize, Size maxSize)
    {
        /// determine width
        if ((maxSize.Width > MaxImageSize.Width) || 
            (maxSize.Width < MinImageSize.Width))
        {
            maxSize.Width = MaxImageSize.Width;
        }
        if ((minSize.Width < MinImageSize.Width) || 
            (minSize.Width > maxSize.Width))
        {
            minSize.Width = MinImageSize.Width;
        }
        int width = _rand.Next(minSize.Width, maxSize.Width + 1);

        /// determine height
        if ((maxSize.Height > MaxImageSize.Height) || 
            (maxSize.Height < MinImageSize.Height))
        {
            maxSize.Height = MaxImageSize.Height;
        }
        if ((minSize.Height < MinImageSize.Height) || 
            (minSize.Height > maxSize.Height))
        {
            minSize.Height = MinImageSize.Height;
        }
        int height = _rand.Next(minSize.Height, maxSize.Height + 1);

        /// the result
        return new Size(width, height);
    }

    public const TextStyleEnum DefaultTextStyle = 
        TextStyleEnum.Chalkboard;

    public static TextStyleEnum GetRandomTextStyle(
        params TextStyleEnum[] usedValues)
    {
        TextStyleEnum textStyle = DefaultTextStyle;

        if (0 == usedValues.Length)
        {
            int max = Enum.GetValues(typeof(TextStyleEnum)).Length;

            textStyle = (TextStyleEnum)(_rand.Next(max));
        }
        else if (1 == usedValues.Length)
        {
            textStyle = usedValues[0];
        }
        else
        {
            int max = usedValues.Length;
            int index = _rand.Next(max);

            textStyle = usedValues[index];
        }

        return textStyle;
    }
}

Giải thích

RandomizationHelper là một lớp tiện ích nhỏ bạn có thể sử dụng trong dự án của bạn để đơn giản hoá việc sinh ngẫu nhiên nhiều tham số khác nhau của CAPTCHA.

Các tham số CAPTCHA lấy các giá trị là số hoặc tập hợp, lớp này cung cấp phương thức trả về giá trị ngẫu nhiên. Mỗi tham số có một giá trị mặc định, và các tham số dạng số có giá trị nhỏ nhất và lớn nhất được định nghĩa trước.

Tất cả các phương thức sinh ngẫu nhiên dùng nhiều tham số khác nhau, thông qua phương thức overloads hoặc từ khoá params.

Phương thức trả về giá trị số (ví dụ, CodeLength) hoạt động như sau:

  • Nếu không có tham số nào được truyền, giá trị ngẫu nhiên được chọn giữa giá trị nhỏ nhất và giá trị lớn nhất định nghĩa trước.
  • Nếu một giá trị được truyền, giá trị ngẫu nhiên được chọn giữa giá trị nhỏ nhất định nghĩa trước và giá trị truyền vào như là giá trị lớn nhất. Chú ý rằng giá trị truyền vào phải nhỏ hơn giá trị lớn nhất được định nghĩa trước.
  • If two values are given as parameters, the random value is selected between the first value as the minimum and the second value as the maximum. Chú ý rằng giá trị nhỏ nhất được truyền vào phải lớn hơn giá trị nhỏ nhất được định nghĩa trước, và giá trị lớn nhất truyền vào phải nhỏ hơn giá trị lớn nhất được định nghĩa trước.

Phương thức trả về giá trị trong tập hợp (ví dụ, CodeType) hoạt động như sau:

  • Nếu không có tham số nào được truyền, giá trị ngẫu nhiên được chọn từ các giá trị có thể.
  • Nếu một giá trị được truyền, nó được trả về như là kết quả - khi bạn truyền vào một giá trị thì sẽ không có sự ngẫu nhiên hoá nào cả. Và nếu bạn muốn dùng một giá trị thì chỉ việc gán trực tiếp vào tham số.
  • Nếu một tập hợp (ví dụ như mảng) các giá trị được truyền, giá trị ngẫu nhiên được chọn trong tập hợp đó.

Dĩ nhiên là bạn có thể sử dụng mã này trực tiếp trong dự án của bạn nhưng đóng gói nó trong lớp RandomizationHelper làm việc đọc mã nguồn dễ dàng hơn và tăng tính tái sử dụng.

Web.config

Mã nguồn đầy đủ

<?xml version="1.0"?>
<!-- 
    Note: As an alternative to hand editing this file you can use the 
    web admin tool to configure settings for your application. Use
    the Website->Asp.Net Configuration option in Visual Studio.
    A full list of settings and comments can be found in 
    machine.config.comments usually located in 
    \Windows\Microsoft.Net\Framework\v2.x\Config 
-->
<configuration>
  <connectionStrings/>
  <system.web>
    <httpHandlers>
      <add verb="*" path="LanapCaptcha.aspx" 
        type="Lanap.BotDetect.CaptchaHandler, Lanap.BotDetect"/>
    </httpHandlers>
    <sessionState mode="InProc" cookieless="AutoDetect" timeout="20" 
      sessionIDManagerType="Lanap.BotDetect.Persistence.
      CustomSessionIDManager, Lanap.BotDetect" />
    <!-- 
      Set compilation debug="true" to insert debugging 
      symbols into the compiled page. Because this 
      affects performance, set this value to true only 
      during development.
    -->
    <compilation debug="false">
      <assemblies>
        <add assembly="System.Design, Version=2.0.0.0, 
          Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
      </assemblies>
    </compilation>
    <!--
      The <authentication> section enables configuration 
      of the security authentication mode used by 
      ASP.NET to identify an incoming user. 
    -->
    <authentication mode="None"/>
    <!--
      The <customErrors> section enables configuration 
      of what to do if/when an unhandled error occurs 
      during the execution of a request. Specifically, 
      it enables developers to configure html error pages 
      to be displayed in place of a error stack trace.

      <customErrors mode="RemoteOnly" 
        defaultRedirect="GenericErrorPage.htm">
        <error statusCode="403" redirect="NoAccess.htm" />
        <error statusCode="404" redirect="FileNotFound.htm" />
      </customErrors>
    -->
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <remove name="LanapCaptchaHandler" />
      <add name="LanapCaptchaHandler" preCondition="integratedMode" 
        verb="*" path="LanapCaptcha.aspx" 
        type="Lanap.BotDetect.CaptchaHandler, Lanap.BotDetect" />
    </handlers>
  </system.webServer>
</configuration>

Giải thích

Những dòng cần thiết để BotDetect CAPTCHA hoạt động đúng được tô đậm, những dòng khác là giá trị chuẩn được sinh ra bởi Visual Studio 2005. Không có thiết lập đặc biệt nào liên quan đến sinh ngẫu nhiên CAPTCHA trong file web.config.

Thành tố <httpHandlers> đăng ký đường dẫn sử dụng cho hình ảnh và âm thanh CAPTCHA được xử lý bởi Lanap.BotDetect.dll code. Thành tố <system.webServer> được sử dụng với cùng mục đích, nhưng chỉ được sử dụng cho IIS 7.0.

Khai báo validateIntegratedModeConfiguration="false" đảm bảo rằng file web.config có thể được xử lý bởi phiên bản cũ hơn của IIS (5.1, 6.0) cũng như phiên bản 7.0. Vì cú pháp đăng ký HttpHandler khác nhau giữa các phiên bản IIS và chế độ tích hợp ASP.NET, dùng cả hai thành tố trên giúp cho file web.config tương thích với tất cả các phiên bản IIS được hỗ trợ.

Thành tố <sessionState> khai báo cơ chế lưu trữ được sử dụng bởi BotDetect để lưu mã CAPTCHA và thiết lập cho mỗi người dùng. Thuộc tính Session State modes, providers, timeouts và cookieless attribute có thể được sử dụng, nhưng thành tố sessionIDManagerType được sử dụng để sửa lỗi gây bởi Windows Media Player 11 khi yêu cầu âm thanh CAPTCHAs (như được giải thích trong mục hỏi đáp).

Phiên bản hiện tại của BotDetect

Xin lưu ý

Trang này là bản dịch tiếng Việt không chính thức của trang gốc tiếng Anh: BotDetect ASP.NET 2.0 CAPTCHA Randomization C# Code Sample và có thể không chính xác, không đầy đủ hoặc không cập nhật.

Cập nhật ngày 2009-11-30. Áp dụng cho BotDetect ASP.NET CAPTCHA v2.0.15 và BotDetect ASP CAPTCHA v2.0.9.

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