用HttpHandler实现asp.net 的验证码功能

321 views 2 comments about 11 years ago Raymond Tang

由于现在的注册机、发帖机等垃圾软件实在太多,为了有效的拦截这些信息,许多站点都需要用到验证码,下面就用HttpHandler实现的验证码机制

/* ***********************************************
* Author:          1987raymond
* Team:            Juice Sharing
* Created Time:    2009-3-10 19:22:46
* CopyRight:       Juice Sharing 团队版权所有 保留一切权利
* NameSpace:       Juice.Common.HttpHandlers
* Class/Interface: ValidateCodeRender
* ***********************************************/

using System;
using System.Text;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web.SessionState;

namespace Juice.Common.HttpHandlers
{
    /// <summary>
    /// 验证码生成器
    /// </summary>
    public sealed class ValidateCodeRender : IHttpHandler, IRequiresSessionState
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public ValidateCodeRender()
        { }

        #region 私有字段

        /// <summary>
        /// 验证码的长度
        /// </summary>
        private int m_CodeLength = 5;

        /// <summary>
        /// 保存到会话状态中的值
        /// </summary>
        private string m_SessionName = "validateCode";

        /// <summary>
        /// 图像的宽
        /// </summary>
        private int m_ImageWidth = 160;

        /// <summary>
        /// 图像的长
        /// </summary>
        private int m_ImageHeight = 60;

        /// <summary>
        /// 图像背景色
        /// </summary>
        private Color m_BackgroundColor = Color.FromArgb(212, 236, 189);

        /// <summary>
        /// 图像上验证码的颜色
        /// </summary>
        private Color m_CodeColor = Color.FromArgb(132, 199, 1);

        /// <summary>
        /// 干扰图形的颜色
        /// </summary>
        private Color m_ObstructionColor = Color.Gray;

        /// <summary>
        /// 用来生成随机验证码的字符串
        /// </summary>
        private const string m_CodeString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890我和你的大小";

        #endregion

        #region IHttpHandler 成员

        /// <summary>
        /// 是否可以重用
        /// </summary>
        public bool IsReusable
        {
            get { return true; }
        }

        /// <summary>
        /// 处理请求
        /// </summary>
        /// <param name="context"></param>
        public void ProcessRequest(HttpContext context)
        {
            ///设置输出的格式
            context.Response.ContentType = "image/gif";
            Random random = new Random();
            ///用来保存随机取得的字符串
            StringBuilder s = new StringBuilder();
            ///创建绘图的图像
            using (Bitmap bitmap = new Bitmap(this.m_ImageWidth, this.m_ImageHeight))
            {
                ///创建绘图图面,用此对象将各种符号等绘制到制定的图像上
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    ///生成背景颜色
                    graphics.FillRectangle(new SolidBrush(this.m_BackgroundColor), 0, 0, this.m_ImageWidth, this.m_ImageHeight);

                    #region 生成验证码
                    ///验证码的字体
                    using (Font font = new Font(FontFamily.GenericSerif, 32, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Pixel))
                    {
                        for (int i = 0; i < this.m_CodeLength; i++)
                        {
                            s.Append(ValidateCodeRender.m_CodeString.Substring(random.Next(0, m_CodeString.Length - 1), 1));
                            ///将字符绘制到图像上
                            graphics.DrawString(s[s.Length - 1].ToString(), font, new SolidBrush(this.m_CodeColor), i * 32, random.Next(0, 24));

                        }
                    }
                    #endregion

                    #region 绘制干扰图形和噪点

                    //干扰图形绘制画笔
                    using (Pen pen = new Pen(new SolidBrush(this.m_ObstructionColor), 1))
                    {
                        for (int i = 0; i < 10; i++)
                        {
                            graphics.DrawLine(pen, new Point(random.Next(0, this.m_ImageWidth - 1), random.Next(0, this.m_ImageHeight - 1)), new Point(random.Next(0, this.m_ImageWidth - 1), random.Next(0, this.m_ImageHeight - 1)));

                        }
                    }
                    for (int i = 0; i < 100; i++)
                    {

                        bitmap.SetPixel(random.Next(this.m_ImageWidth), random.Next(this.m_ImageHeight), Color.FromArgb(random.Next()));
                    }


                    #endregion

                    ///保存图像到输出流
                    bitmap.Save(context.Response.OutputStream, ImageFormat.Gif);
                }
            }
            ///将验证码的值保存到用户会话状态中,并且不区分大小写
            context.Session[this.m_SessionName] = s.ToString().ToLower();
            context.Response.End();
        }

        #endregion
    }
}

上面这个类就是绘制验证码图像并且输出到页面的类,它实现了IHttpHandler接口

下面再到web.config的 <httpHandlers>节点中添加此项:
      <add verb="GET" path="ValidateCode.aspx" validate="false" type="Juice.Common.HttpHandlers.ValidateCodeRender,Juice.Common" />

Juice.Common.HttpHandlers.ValidateCodeRender,Juice.Common 根据你自己的情形而定

下面则是在具体的页面中调用:

测试验证码:<img src="/ValidateCode.aspx" alt="验证码" id="validateCode" /><a
                    href="javascript:refreshCode();">刷新验证码</a>
<script language="javascript" type="text/javascript">
                function refreshCode()
                {
                var e=gid("validateCode");
                e.src="/ValidateCode.aspx"+"?t="+new Date().toTimeString();
                }
                </script>
效果如下图所示:

大家注意下这个地方:

///将验证码的值保存到用户会话状态中,并且不区分大小写
            context.Session[this.m_SessionName] = s.ToString().ToLower();

我是把验证码保存在Session中的,所以如果你需要检查某个用户输入是否正确就只需看其值和Session中的值是不是一样的

同时有一点注意下就是我看到一些网上教程所把验证码的值保存在Cookie中,我想这样做是错误的,而且丧失了验证码的功能,因为Cookie是客户端也可以访问的

大家还可以扩展验证码 的内容,比如改成算术等等的形式

Add comment

Comments (2)

Ra*** about 7 years ago

@小小小菜鸟 你好 能否将你的代码贴出来,这样帮你分析。我的此验证码功能已经改用到在线网站Mvc用的版本

小小*** about 7 years ago

请问 我也做了一个类似的验证码 img标签 但是 每次ajax刷新的时候 都要提交两次 后来找到是img标签的问题
把img标签屏蔽了 就不会屏蔽了 就不会重复提交 请问 怎么img标签在页面的时候防止ajax重复提交