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( 4 6 );
        
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( 0 0 this .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( 0 0 );
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( 0 255 );
}
SolidBrush sBrush 
=   new  SolidBrush(Color.FromArgb(red, green, blue));
g.FillPath(sBrush, path);



最后,扭曲图像。

Code:

//  Iterate over every pixel
double  distort  =  random.Next( 5 20 *  (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,在处理的时候自动加上#。

[总结]

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

转载于:https://www.cnblogs.com/juan/archive/2009/03/28/1423883.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值