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

  • 相关阅读:
    小程序页面跳转 之 js页面函数绑定跳转
    win10系统激活提示无法连接到你组织的激活服务器如何解决
    小程序请求后端接口实例
    cors解决跨域问题
    aes加密 工具类
    后端解决跨域的问题
    理解传输层中UDP协议首部校验和以及校验和计算方法的Java实现
    常见面试题之操作系统中的LRU缓存机制实现
    Java算法之根据二叉树不同遍历结果重建二叉树
    基于Java的二叉树层序遍历打印实现
  • 原文地址:https://www.cnblogs.com/juan/p/1423883.html
Copyright © 2011-2022 走看看