首页 > ASP.Net > 综合技巧 > 正文
ASP.NET下实现英文及中文汉字的验证码控件
来源:MSPROJECT  时间:2008-01-16  作者:-  点击次数:125
本文实现了一个非常好的验证码控件,它向客户端浏览器传递一个包含随机产生的文字的图片,并验证用户的输入。另外,译者还在修改了原文中的代码,实现了中文汉字的验证码控件。

ASP.NET下英文及中文汉字的验证码控件
关键原理
要在这篇文章中详细解释所有的原理是非常困难的,所以,我将把最关键的地方给你讲解一下。
1. 创建一个可以render <img>标签的用户控件
2. 产生一个随机的文字,并生成图片发送给客户浏览器
3. 在后台验证用户的输入

创建一个可以render <img>标签的用户控件
这个控件不但要能产生<img>标签,还要能动态地产生url,将其付给src属性。
实现的代码如下:

namespace NatsNet.Web.UI.Controls
{
    [DefaultProperty("Text")] [ToolboxData("<{0}:ImageVerifier runat=server>")] 

    public class ImageVerifier : WebControl, IHttpHandler
    {
        private string m_UniqueID = string.Empty;
        public ImageVerifier(): base(HtmlTextWriterTag.Img) { }
        private string MyUniqueID  { ... }
        public string Text { ... }        
        private string GetRandomText() { ... }
       
        protected override void OnInit(EventArgs e) { ... }        
        protected override void LoadControlState(object savedState) { ... }        
        protected override object SaveControlState() { ... }        
        protected override void Render(HtmlTextWriter output) { ... }

        public void ProcessRequest(HtmlTextContext context) { ... }        
        public bool IsReusable { ... }
    }
}
这个类继承于WebControl和IHttpHandler。重写的render方法将会负责Render控件:

protected override void Render(HtmlTextWriter output){    output.AddAttribute(HtmlTextWriterAttribute.Src, "ImageVerifier.axd?uid=" + this.MyUniqueID);    base.Render(output);    output.Write("<script language=javascript>");    output.Write("function RefreshImageVerifier(id,srcname)");    output.Write("{ var elm = document.getElementById(id);");    output.Write("  var dt = new Date();");    output.Write("  elm.src=srcname + &ts= + dt;");    output.Write("  return false;");    output.Write("}</script>");    output.Write("&nbsp;<a href=# onclick=\"return RefreshImageVerifier("        + this.ClientID + ",ImageVerifier.axd?&uid="        + this.MyUniqueID + ");\">Refresh</a>");}随机产生文本,并生成图片发送

当浏览器请求上面的<img>标签中的图像资源的时候,ImageVerifier类中的public void ProcessRequest(HttpContext context)会被调用。为了让它被调用,你需要在web.config文件中配置一个HttpHandler。

<httpHandlers>    <add        verb="GET"        path="ImageVerifier.axd"        type="NatsNet.Web.UI.Controls.ImageVerifier, NatsNet.Web.UI.Controls"        /></httpHandlers> 下面是ProcessRequest() 方法:

public void ProcessRequest(HttpContext context)
{
    Bitmap bmp = new Bitmap(180, 40);
    Graphics g = Graphics.FromImage(bmp);
    string randString = GetRandomText();
    string myUID = context.Request["uid"].ToString();

    if (context.Cache[myUID] == null)
        context.Cache.Add(  myUID,
                            randString,
                            null,
                            Cache.NoAbsoluteExpiration,
                            TimeSpan.FromMinutes(5),
                            System.Web.Caching.CacheItemPriority.Normal,
                            null
                        );
    else
        context.Cache[myUID] = randString;
       
    g.FillRectangle(Brushes.WhiteSmoke,0, 0, 180, 40);   
    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
    g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
   
    Random rand = new Random();   
    for (int i = 0; i < randString.Length; i++)
    {
        Font drawFont = new Font("Arial", 18,
            FontStyle.Italic | (rand.Next() % 2 == 0 ? FontStyle.Bold : FontStyle.Regular));
        g.DrawString(randString.Substring(i,1), drawFont, Brushes.Black, i * 35 + 10, rand.Next()% 12);

        Point[] pt = new Point[15];
        for (inti = 0; i < 15; i++)
        {
            pt[i] = newPoint(rand.Next() % 180, rand.Next() % 35);
            g.DrawEllipse(Pens.LightSteelBlue,pt[i].X, pt[i].Y, rand.Next() % 30 + 1, rand.Next() % 30 + 1);
        }
       
        context.Response.Clear();
        context.Response.ClearHeaders();
        context.Response.ContentType = "image/jpeg";
        bmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);       
        context.Response.End();
    }
}在上面的方法中,GetRandomText()会随机产生文字。在这里,我使用了GUID的方法产生随机文字。

private string GetRandomText()
{
    string uniqueID = Guid.NewGuid().ToString();
    string randString = "";
   
    for (int i = 0, j = 0; i < uniqueID.Length && j < 5; i++)
    {
        char l_ch = uniqueID.ToCharArray()[i];
        if ((l_ch >= A && l_ch <= Z) || (l_ch >= a && l_ch <= z) || (l_ch >=  0 && l_ch <= 9))
        {
            randString += l_ch;
            j++;
        }
    }
   
    return randString;
}验证用户的输入
最终,我们需要将用户的输入和这些随机文字比较。为了达到这个目的,我在我们的控件中添加了一个Text属性,这个属性得到存储在Cache中的随机文字,并返回这个字符串。我们可以使用它来验证用户输入是否正确。

public string Text
{
    get
    {
        return string.Format("{0}",
        HttpContext.Current.Cache[this.MyUniqueID]);
    }
}汉字的验证码
我在翻译文章的时候,突然想到为什么不顺便把它实现成中文汉字的验证码呢?这样虽然让用户用起来有点困难,不过不失为一个技术上的探讨。于是我搜索一般,终于发现了一篇汉字码生成的文章用C#生成随机中文汉字验证码的基本原理。

我把它们两个结合了一下,修改了其GetRandomText()方法,于是就实现了,文章中的另一个代码,可以在ASP.NET下实现中文汉字的验证码。
 

 

private string GetRandomText()
{
    //获取GB2312编码页(表)
    Encoding gb = Encoding.GetEncoding("gb2312");

    //调用函数产生4个随机中文汉字编码
    object[] bytes = CreateRegionCode(5);

    //根据汉字编码的字节数组解码出中文汉字
    string str1 = gb.GetString((byte[])Convert.ChangeType(bytes[0], typeof(byte[])));
    string str2 = gb.GetString((byte[])Convert.ChangeType(bytes[1], typeof(byte[])));
    string str3 = gb.GetString((byte[])Convert.ChangeType(bytes[2], typeof(byte[])));
    string str4 = gb.GetString((byte[])Convert.ChangeType(bytes[3], typeof(byte[])));
    string str5 = gb.GetString((byte[])Convert.ChangeType(bytes[4], typeof(byte[])));
   
    //string uniqueID = Guid.NewGuid().ToString();
    string randString = "";
    randString = str1 + str2 + str3 + str4 + str5;

    return randString;
}至于CreateRegionCode( )函数是如何产生汉字字符串的原理, 请参加原文!