zoukankan      html  css  js  c++  java
  • ASP.NET 2.0 下的验证码控件

    [简介]

    验证码是阻止恶意用户采用自动注册机/发帖机的一个好方法,也许你已经在Google,yahoo等大型网站上见到它的应用了。本文会给你一个这样的控件。

    [源代码]

    我使用的第一个验证码控件是BrainJar写的CAPTCHA Image article,在此之后,我又读了文章MSDN HIP challenge article,并在我的代码中做了不少 的改动。本文中的代码就是基于MSDN HIP的文章。

    [实现方法]

    Captcha.ascx 是一个用户控件文件。当加载的时候,调用SetCaptcha()方法。
    RandomText 类产生随机文字
    RNG 类产生随机数字
    CaptchaImage 类产生图像
    Encryptor 用于加密和解密
    Captcha.ashx 返回图像

    下面讨论这些组建:

    [用户控件]

    用户控件中的主要方法是SetCaptcha(),无论何时,只要你需要改变图像或者加载图片,它会被执行。private
    Code:
    void SetCaptcha()
    {
        
    // Set image
        string s = RandomText.Generate();

        
    // Encrypt
        string ens = Encryptor.Encrypt(s, "srgerg$%^bg"
                     Convert.FromBase64String(
    "srfjuoxp"));

        
    // Save to session
        Session["captcha"= s.ToLower();
            
        
    // Set url
        imgCaptcha.ImageUrl = "~/Captcha.ashx?w=305&h=92&c=" + 
                              ens 
    + "&bc=" + color;
    }



    这个方法会使用一个密钥加密文字,在本文代码中这个密钥是在文件中写死的,如果你想动态改变它,可以将其存放在数据库中。这个方法还向Session中保存文字,以在用户输入之后比较。

    另外用户控件还有两个属性,用以控制其风格:
    Style
    Background color


    两个事件处理句柄处理成功和失败事件,我们在这些事件上使用委托:

    Code:
    public delegate void CaptchaEventHandler();



    当用户点击提交的时候,btnSubmit_Click() 验证其输入:

    Code:
    protected void btnSubmit_Click(object s, EventArgs e)
    {
        
    if (Session["captcha"!= null && txtCaptcha.Text.ToLower() == 
        Session[
    "captcha"].ToString())
        {
            
    if (success != null)
            {
                success();
            }
        }
        
    else
        {
            txtCaptcha.Text 
    = "";
            SetCaptcha();

            
    if (failure != null)
            {
                failure();
            }
        }
    }



    RNG 类

    RNG 类使用RNGCryptoServiceProvider类产生随机数字。

    Code:
    public static class RNG
    {
        
    private static byte[] randb = new byte[4];
        
    private static RNGCryptoServiceProvider rand 
                       
    = new RNGCryptoServiceProvider();

        
    public static int Next()
        {
            rand.GetBytes(randb);
            
    int value = BitConverter.ToInt32(randb, 0);
            
    if (value < 0) value = -value;
            
    return value;
        }
        
    public static int Next(int max)
        {
            
    // 
        }
        
    public static int Next(int min, int max)
        {
            
    // 
        }
    }



    RandomText 类

    为了产生随机性很强的文字,我们使用RNG类产生随机数,从一个字符数组中取字符。我发现这是 CryptoPasswordGenerator中一个很有用的技术。

    Code:
    public static class RandomText
    {
        
    public static string Generate()
        {
            
    // Generate random text
            string s = "";
            
    char[] chars = "abcdefghijklmnopqrstuvw".ToCharArray() + 
                           
    "xyzABCDEFGHIJKLMNOPQRSTUV".ToCharArray() + 
                           
    "WXYZ0123456789".ToCharArray();
            
    int index;
            
    int lenght = RNG.Next(46);
            
    for (int i = 0; i < lenght; i++)
            {
                index 
    = RNG.Next(chars.Length - 1);
                s 
    += chars[index].ToString();
            }
            
    return s;
        }
    }


    CaptchaImage 类

    该类是本控件的核心,它得到图像文字、尺寸、背景颜色,然后产生图像。

    主要的方法是GenerateImage(),它根据我们提供的信息产生图片。
    Code:
    private void GenerateImage()
    {
        
    // Create a new 32-bit bitmap image.
        Bitmap bitmap = new Bitmap(this.width, this.height, 
            PixelFormat.Format32bppArgb);

        
    // Create a graphics object for drawing.
        Graphics g = Graphics.FromImage(bitmap);
        Rectangle rect 
    = new Rectangle(00this.width, this.height);
        g.SmoothingMode 
    = SmoothingMode.AntiAlias;

        
    // Fill background
        using (SolidBrush b = new SolidBrush(bc))
        {
            g.FillRectangle(b, rect);
        }



    首先,声明Bitmap Graphics 对象,以及一个和Bitmap 对象一样尺寸的Rectangle 。然后使用SolidBrush填充背景。

    现在,我们需要设置字体大小,因为,字体是从fonts字体集合中随机选择的。

    Code:
    // Set up the text font.
    int emSize = (int)(this.width * 2 / text.Length);
    FontFamily family 
    = fonts[RNG.Next(fonts.Length - 1)];
    Font font 
    = new Font(family, emSize);

    // Adjust the font size until
    // the text fits within the image.
    SizeF measured = new SizeF(00);
    SizeF workingSize 
    = new SizeF(this.width, this.height);

    while (emSize > 2 &&
          (measured 
    = g.MeasureString(text, font)).Width 
           
    > workingSize.Width || measured.Height 
           
    > workingSize.Height)
    {
        font.Dispose();
        font 
    = new Font(family, emSize -= 2);
    }



    下一步使用GraphicsPath 绘制文字。

    Code:
    GraphicsPath path = new GraphicsPath();
    path.AddString(
    this.text, font.FontFamily, 
            (
    int)font.Style, font.Size, rect, format);



    最重要的部分是给文字加颜色及扭曲文字:随机选择0-255之间的数字,然后使用这个RGB值给文字设置颜色。并且,需要检查颜色和背景色是否能够区分。

    Code:
    // Set font color to a color that is visible within background color
    int bcR = Convert.ToInt32(bc.R);
    int red = random.Next(256), green = random.Next(256), blue = 
        random.Next(
    256);
    // This prevents font color from being near the bg color
    while (red >= bcR && red - 20 < bcR ||
        red 
    < bcR && red + 20 > bcR)
    {
        red 
    = random.Next(0255);
    }
    SolidBrush sBrush 
    = new SolidBrush(Color.FromArgb(red, green, blue));
    g.FillPath(sBrush, path);



    最后,扭曲图像。

    Code:

    // Iterate over every pixel
    double distort = random.Next(520* (random.Next(10== 1 ? 1 : -1);

    // Copy the image so that we're always using the original for 
    // source color
    using (Bitmap copy = (Bitmap)bitmap.Clone())
    {
        
    for (int y = 0; y < height; y++)
        {
            
    for (int x = 0; x < width; x++)
            {
                
    // Adds a simple wave
                int newX = 
                  (
    int)(x + (distort * Math.Sin(Math.PI * y / 84.0)));
                
    int newY = 
                  (
    int)(y + (distort * Math.Cos(Math.PI * x / 44.0)));
                
    if (newX < 0 || newX >= width)
                    newX 
    = 0;
                
    if (newY < 0 || newY >= height)
                    newY 
    = 0;
                bitmap.SetPixel(x, y, 
                copy.GetPixel(newX, newY));
            }
        }
    }



    Captcha.ashx

    这个HTTP 句柄得到需要创建验证码图像的信息,然后创建一个。注意:它接收到的是加密后的文字,并使用密钥解密。

    Code:
    public class Captcha : IHttpHandler 
    {  
        
    public void ProcessRequest (HttpContext context) {
            context.Response.ContentType 
    = "image/jpeg";
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.BufferOutput 
    = false;
            
            
    // Get text
            string s = "No Text";
            
    if (context.Request.QueryString["c"!= null &&
                context.Request.QueryString[
    "c"!= "")
            {
                
    string enc = context.Request.QueryString["c"].ToString();
                
                
    // space was replaced with + to prevent error
                enc = enc.Replace(" ""+");
                
    try
                {
                    s 
    = Encryptor.Decrypt(enc, "srgerg$%^bg"
                Convert.FromBase64String(
    "srfjuoxp"));
                }
                
    catch { }
            }
            
    // Get dimensions
            int w = 120;
            
    int h = 50;
            
    // Width
            if (context.Request.QueryString["w"!= null &&
                context.Request.QueryString[
    "w"!= "")
            {
                
    try
                {
                    w 
    = Convert.ToInt32(context.Request.QueryString["w"]);
                }
                
    catch { }
            }
            
    // Height
            if (context.Request.QueryString["h"!= null &&
                context.Request.QueryString[
    "h"!= "")
            {
                
    try
                {
                    h 
    = Convert.ToInt32(context.Request.QueryString["h"]);
                }
                
    catch { }
            }
            
    // Color
            Color Bc = Color.White;
            
    if (context.Request.QueryString["bc"!= null &&
                context.Request.QueryString[
    "bc"!= "")
            {
                
    try
                {
                    
    string bc = context.Request.QueryString["bc"].
                ToString().Insert(
    0"#");
                    Bc 
    = ColorTranslator.FromHtml(bc);
                }
                
    catch { }
            }
            
    // Generate image
            CaptchaImage ci = new CaptchaImage(s, Bc, w, h);
            
            
    // Return
            ci.Image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
            
    // Dispose
            ci.Dispose();
        }

        
    public bool IsReusable
        {
            
    get
            {
                
    return true;
            }
        }
    }



    这里有两点需要说明:
    1. 因为,在URL中,'+'意思是空格,所以我们把空格都替换成+
    2. #在URL中会产生问题,我们不发送颜色值中的#,例如:当颜色是#ffffff的时候,我们只发送ffffff,在处理的时候自动加上#。

    [总结]

    在文章的源代码中,除了控件的源代码,你还可以见到一个使用这个控件的例子。
    https://files.cnblogs.com/jueban/QQ(1).rar

  • 相关阅读:
    Codevs 2296 仪仗队 2008年省队选拔赛山东
    Codevs 1535 封锁阳光大学
    Codevs 1069 关押罪犯 2010年NOIP全国联赛提高组
    Codevs 1218 疫情控制 2012年NOIP全国联赛提高组
    Codevs 1684 垃圾陷阱
    洛谷 P1108 低价购买
    Vijos P1325桐桐的糖果计划
    Codevs 3289 花匠 2013年NOIP全国联赛提高组
    Codevs 2611 观光旅游(floyed最小环)
    C语言基础之彩色版C语言(内含linux)
  • 原文地址:https://www.cnblogs.com/juan/p/1423883.html
Copyright © 2011-2022 走看看