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

翻译
Nataraj K. 著Image Verifier - Custom Control in 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(11, 14);
        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(0, 7);
        }
        else
        ...{
            r2 = rnd.Next(0, 16);
        }
        string str_r2 = rBase[r2].Trim();

        //区位码第3位
        rnd = new Random(r2 * unchecked((int)DateTime.Now.Ticks) + i);
        int r3 = rnd.Next(10, 16);
        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(1, 16);
        }
        else if (r3 == 15)
        ...{
            r4 = rnd.Next(0, 15);
        }
        else
        ...{
            r4 = rnd.Next(0, 16);
        }
        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(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 (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;
            }
        }

    }
}

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

  1. ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇

    第三章 为控件添加事件 后篇 前一篇文章只是简单的说了下事件,但是大家应该方法,在ASP.NET自定义控件中只是简单那么定义事件是行不 通.如果大家开发的是WinForm中的事件,之前的定义可能没有什 ...

  2. [转载]WebForm下使用 jQuery.loadUserControl异步load用户控件

    现在做网站都追求用户体验,那么ajax自然就必不可少.如果您用过Asp.Net MVC ,你会发现Asp.Net MVC 和jQuery 配合的非常默契(事实上jQuery已经成了微软的御用脚本库了) ...

  3. [ASP.NET 控件实作 Day28] 图形验证码控件

    在网页上常把图形验证码应用在登入或贴文的页面中,因为图形验证码具有机器不易识别的特性,可以防止机器人程序恶意的存取网页.在本文中将实作一个图形验证码的服务器控件,透过简单的属性设定就可以轻易地在网页上 ...

  4. 浅谈ASP.NET 4中构造“.NET研究”HTML5视频控件

    在本文中,将一步步地指导你如何使用Visual Studio 2010和ASP.NET 4的相关知识,打造一个基于HTML5标准规范的视频播放控件,其中你会学习到一些关于HTML 5的知识,还会学到如 ...

  5. 新瓶旧酒ASP.NET AJAX(1) - 简单地过一下每个控件(ScriptManager、ScriptManagerProxy

    [索引页] [×××] 新瓶旧酒ASP.NET AJAX(1) - 简单地过一下每个控件(ScriptManager.ScriptManagerProxy.UpdatePanel. UpdatePro ...

  6. MVC3学习第十三章 佟掌柜第二弹——MVC3下利用陕北吴旗娃的分页控件实现数据分页...

    本章学习内容 1.了解陕北吴旗娃的Mvc分页控件 2.利用分页控件实现MVC3下的商品分页 3.利用分页控件实现MVC3下一个页面多个分页以及ajax分页效果 1.了解陕北吴旗娃的Mvc分页控件 在w ...

  7. ASP.NET开源框架之HIPPO技术内幕(五)--控件结构

    五.控件结构 上一章讲的是如何实现多语言的设计,本章来讲一下控件部分. 不管后端的代码多么完善,多么完美,最后都要以一定的形式展示出来,并且要使用灵活,这时就会用到控件. 在ASP的年代,去写这种控件 ...

  8. ASP.NET自定义控件组件开发 第四章 组合控件开发CompositeControl

    第四章 组合控件开发CompositeControl 大家好,今天我们来实现一个自定义的控件,之前我们已经知道了,要开发自定义的控件一般继承三个基 类:Control,WebControl,还有一个就 ...

  9. ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇

    第三章 为控件添加事件 好了,我们之前以前开发一个控件.而且也添加了属性,开发也很规范,但是那个控件还差最后一点:添加事件. 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.N ...

最新文章

  1. wordpress footer置底
  2. 为什么你的年薪只是别人的月薪?你需要技术专家帮你「充电」
  3. mysql5.6 主从配置_CentOS7+mysql5.6配置主从
  4. 一个简单限速器的java实现[1]
  5. Spring web项目中web.xml常用的配置
  6. Git 提交错了不用慌,这三招帮你修改记录
  7. 探究 Linux 内核 dts 设备树定义文件
  8. UML-----构件图与部署图
  9. Springboot毕设项目房屋租赁系统4yvi2(java+VUE+Mybatis+Maven+Mysql)
  10. 赛福基因公开课第二节《神经系统遗传病基因检测简介》
  11. 计算机二级考试题库 操作题,2016计算机二级考试题库:《C++》基本操作题练习...
  12. 海航科技集团要寻人才去火星喽!
  13. Docker 的新变化你知道多少?
  14. python数据分析及可视化(九)pandas数据规整(分组聚合、数据透视表、时间序列、数据分析流程)
  15. CAD/CAM/CAE基础(一) 概论
  16. 智慧化工厂安全风险生产预警系统软件
  17. Tensorflow2中Kares自定义损失函数
  18. 即构科技廖念波:构建产品矩阵,加快音视频技术全面开花
  19. POI-TL使用及工具类
  20. ubuntu18安装oracle11g,在Ubuntu 18.04系统下快速安装Oracle Java 11的方法

热门文章

  1. 陶泓达:3.27晚间美盘最新黄金原油策略分享
  2. Qt OpenGL(09)在着色器中实现旋转的彩色正方体
  3. MyBatis Insert操作(一)
  4. spotify电脑下载歌曲_流媒体音乐平台Spotify部分账号密码泄露 并非数据库泄露而是撞库...
  5. C语言课程设计:小型电子词典
  6. MySQL成勒索新目标,数据服务基线安全问题迫在眉睫 1
  7. 用Azure认知服务开发一个语音翻译机,学英文很爽快
  8. win7系统ftp服务器搭建,win7系统 ftp服务器搭建
  9. 我喜欢的儿歌:小白小白上楼梯
  10. 工控机防病毒该如何进行