ASP.NET下英文及中文汉字的验证码控件

本文转自http://bbs.msproject.cn/Default.aspx?g=posts&t=660

翻译
Nataraj K. 著Image Verifier - Custom Control in ASP.NET

简介
本文实现了一个非常好的验证码控件,它向客户端浏览器传递一个包含随机产生的文字的图片,并验证用户的输入。另外,译者还在修改了原文中的代码,实现了中文汉字的验证码控件。

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='#' οnclick=/"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( )函数是如何产生汉字字符串的原理, 请参加原文!

由于不知道如何上传文件,现将ImageVerifier.cs的整个代码复制,或者请到

http://bbs.msproject.cn/Default.aspx?g=posts&t=660下载源文件。 

 

using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Text;
using  System.Web;
using  System.Web.UI;
using  System.Web.UI.WebControls;
using  System.Drawing;
using  System.Web.Caching;

namespace  NatsNet.Web.UI.Controls
{
    [DefaultProperty(
"Text")]
    [ToolboxData(
"<{0}:ImageVerifier runat=server></{0}:ImageVerifier>")]
    
public class ImageVerifier : WebControl, IHttpHandler
    
{
        
private string m_UniqueID = string.Empty;
        
public ImageVerifier()
            : 
base(HtmlTextWriterTag.Img)
        
{
        }


        
private string MyUniqueID
        
{
            
get
            
{
                
if (m_UniqueID == string.Empty) m_UniqueID = Guid.NewGuid().ToString();
                
return m_UniqueID;
            }

        }

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;
}


/*
    此函数在汉字编码范围内随机创建含两个元素的十六进制字节数组,每个字节数组代表一个汉字,并将
    四个字节数组存储在object数组中。
    参数:strlength,代表需要产生的汉字个数
    
*/

public static object[] CreateRegionCode(int strlength)
{
    
//定义一个字符串数组储存汉字编码的组成元素
    string[] rBase = new String[16"0""1""2""3""4""5""6""7""8""9""a""b""c""d""e""f" };

    Random rnd 
= new Random();

    
//定义一个object数组用来
    object[] bytes = new object[strlength];

    
/**/
    
/*每循环一次产生一个含两个元素的十六进制字节数组,并将其放入bject数组中
         每个汉字有四个区位码组成
         区位码第1位和区位码第2位作为字节数组第一个元素
         区位码第3位和区位码第4位作为字节数组第二个元素
        
*/

    
for (int i = 0; i < strlength; i++)
    
{
        
//区位码第1位
        int r1 = rnd.Next(1114);
        
string str_r1 = rBase[r1].Trim();

        
//区位码第2位
        rnd = new Random(r1 * unchecked((int)DateTime.Now.Ticks) + i);//更换随机数发生器的种子避免产生重复值
        int r2;
        
if (r1 == 13)
        
{
            r2 
= rnd.Next(07);
        }

        
else
        
{
            r2 
= rnd.Next(016);
        }

        
string str_r2 = rBase[r2].Trim();

        
//区位码第3位
        rnd = new Random(r2 * unchecked((int)DateTime.Now.Ticks) + i);
        
int r3 = rnd.Next(1016);
        
string str_r3 = rBase[r3].Trim();

        
//区位码第4位
        rnd = new Random(r3 * unchecked((int)DateTime.Now.Ticks) + i);
        
int r4;
        
if (r3 == 10)
        
{
            r4 
= rnd.Next(116);
        }

        
else if (r3 == 15)
        
{
            r4 
= rnd.Next(015);
        }

        
else
        
{
            r4 
= rnd.Next(016);
        }

        
string str_r4 = rBase[r4].Trim();

        
//定义两个字节变量存储产生的随机汉字区位码
        byte byte1 = Convert.ToByte(str_r1 + str_r2, 16);
        
byte byte2 = Convert.ToByte(str_r3 + str_r4, 16);
        
//将两个字节变量存储在字节数组中
        byte[] str_r = new byte[] { byte1, byte2 };

        
//将产生的一个汉字的字节数组放入object数组中
        bytes.SetValue(str_r, i);

    }


    
return bytes;

}
 
public string Text
{
    
get
    
{
        
return string.Format("{0}", HttpContext.Current.Cache[this.MyUniqueID]);
    }

}


        
protected override void OnInit(EventArgs e)
        
{
            
base.OnInit(e);
            Page.RegisterRequiresControlState(
this);
        }

        
protected override void LoadControlState(object savedState)
        
{
            Pair p 
= savedState as Pair;
            
if (p != null)
            
{
                m_UniqueID 
= p.Second as string;
            }

        }

        
protected override object SaveControlState()
        
{
            Object baseState 
= base.SaveControlState();
            Pair prState 
= new Pair(baseState, this.MyUniqueID);
            
return prState;
        }

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='#' οnclick="return RefreshImageVerifier('" + this.ClientID + "','ImageVerifier.axd?&uid=" + this.MyUniqueID + "');">Refresh</a>");
}


public void ProcessRequest(HttpContext context)
{
    Bitmap bmp 
= new Bitmap(18040);
    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, 
0018040);
    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 (int i = 0; i < 15; i++)
    
{
        pt[i] 
= new Point(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();
}

            
//g.DrawLines(Pens.BlueViolet, pt);
        public bool IsReusable
        
{
            
get
            
{
                
return true;
            }

        }


    }

}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值