网站在进行重要操作步骤时,为了防止攻击,一般都采用生成验证码的方法。为了使用方便,我自己写了一个从BaseValidator实现的验证控件,负责给指定的Image控制设置ImageSrc并在验证失败时,清空TextBox控制的值。从BaseValidator继承,是因为服务端只需调用
就可以了。
下面主要代码公布如下,希望大家拍砖。
使用代码示例:
<asp:TextBox ID="TextBox1" runat="server" AutoCompleteType="Disabled" ></asp:TextBox>
<asp:Image ID="Image1" runat="server" ImageAlign="absMiddle" />
<asp:ImageValidator runat="server" ID="imagevalidator"
ImageControl="Image1"
ControlToValidate="TextBox1"
Display="Dynamic"
CharCount="4"
Fonts="宋体,Gungsuh,仿宋,黑体"
BgColor="White"
MaxFontSize="16"
MinFontSizePercent="50"
ErrorMessage="验证码错误!"
SetFocusOnError="True" />

后台编码:
protected void OnLogin(object sender, EventArgs e)

{
try

{
if (Page.IsValid)

{
string hashedPwd = Utility.Md5.HashedString(this.tbLoginPassword.Text.Trim());
Manager manager = Manager.CreateInstance(this.tbLoginName.Text.Trim(), hashedPwd);
//登陆成功
ManagePageBase.SetAuthSession(manager);
System.Web.Security.FormsAuthentication.RedirectFromLoginPage(manager.Name, false);
}
}
catch (BusinessException ex)

{
this.ShowMessage(ex.Message);
}
}

实现代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Web.Configuration;
using System.Drawing.Design;
using System.Drawing;
using System.Web.SessionState;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace Iyond.Web.UI.WebControls


{
public class ImageValidator : System.Web.UI.WebControls.BaseValidator

{

private string _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
[Bindable(false)]
[Category("Data")]
[DefaultValue("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")]
[Localizable(true)]
[Description("用于生成验证码的源字符")]
public string Chars

{
get

{
return _chars;
}

set

{
_chars = value;
}
}

private int _charCount = 5;
[Bindable(false)]
[Category("Data")]
[DefaultValue(5)]
[Localizable(true)]
[Description("验证码中字符的数量")]
public int CharCount

{
get

{
return _charCount;
}
set

{
_charCount = value;
}
}

private string _pageValidateCode = "~/IyondValidateCode.aspx";
[Category("Data")]
[Localizable(true)]
[Description("在Web.config中的HttpHandler节中配置的读取验证码的页面")]
public string ValidateCodePage

{

get
{ return _pageValidateCode; }

set
{ _pageValidateCode = value; }
}


private int _minFontSizePercent = 60;
[Category("Data")]
[Localizable(true)]
[Description("字体最小高度占Image控件高度的百分比")]
public int MinFontSizePercent

{
get

{
return _minFontSizePercent;
}
set

{
if (value > 100)
throw new ArgumentOutOfRangeException("MaxFontSize", value, "最大为100(表示100%)");
if (value < 50)
throw new ArgumentOutOfRangeException("MaxFontSize", value, "最小为50(表示50%)");

_minFontSizePercent = value;
}
}


public int MinFontSize

{
get

{
return Convert.ToInt32(this.MaxFontSize * this.MinFontSizePercent / 100 + 1);
}
}

private int _maxFontSize = 100;
[Category("Data")]
[Localizable(true)]
[Description("字体最大高度占Image控件高度的百分比")]
public int MaxFontSize

{
get

{
return _maxFontSize;
}
set

{
if (value > 100)
throw new ArgumentOutOfRangeException("MaxFontSize", value, "最大为100");
if (value < 6)
throw new ArgumentOutOfRangeException("MaxFontSize", value, "最小为6");
_maxFontSize = value;
}
}

private Color _fontColor = Color.Black;
[Category("Data")]
[Localizable(true)]
[Description("字体颜色")]
public Color FontColor

{
get

{
return _fontColor;
}
set

{
_fontColor = value;
}
}

private Color _bgColor = Color.White;
[Category("Data")]
[Localizable(true)]
[Description("字体颜色")]
public Color BgColor

{
get

{
return _bgColor;
}
set

{
_bgColor = value;
}
}

private bool _ignoreCase = true;
[Category("Data")]
[Localizable(true)]
[Description("是否乎略大小写")]
public bool IgnoreCase

{

get
{ return _ignoreCase; }

set
{ _ignoreCase = value; }
}

private bool _enableChangeImage = true;
[Category("Data")]
[Localizable(true)]
[Description("是否允许更换图片")]
public bool EnableChangeImage

{

get
{ return _enableChangeImage; }

set
{ _enableChangeImage = value; }
}

private int _maxRotate = 45;
[Category("Data")]
[Localizable(true)]
[Description("每个字的最大旋转角度")]
public int MaxRotate

{

get
{ return _maxRotate; }

set
{ _maxRotate = value; }
}

private List<string> _fonts = new List<string>();
[Category("Data")]
[Localizable(true)]
[Description("字体列表,从中随机选择")]
[Editor("System.Windows.Forms.Design.StringArrayEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
typeof(UITypeEditor)), TypeConverter(typeof(FontNamesConverter)),
RefreshProperties(RefreshProperties.Repaint),
NotifyParentProperty(true)]
public string[] Fonts

{
get

{
return _fonts.ToArray();
}
set

{
if (value == null)
throw new ArgumentNullException("Fonts");

_fonts.Clear();
_fonts.AddRange(value);
}
}



private static Dictionary<string, ImageValidatorConfig> runtimeConfig = new Dictionary<string, ImageValidatorConfig>();


/**//// <summary>
/// 查询验证码配置
/// </summary>
/// <param name="sign">验证码标识</param>
/// <returns></returns>
public static ImageValidatorConfig GetConfig(string sign)

{
if (!runtimeConfig.ContainsKey(sign))
throw new ArgumentOutOfRangeException("sign", sign);

return runtimeConfig[sign] as ImageValidatorConfig;
}



/**//// <summary>
/// 计算是不是合法(验证码是不是正确)
/// </summary>
/// <returns></returns>
protected override bool EvaluateIsValid()

{
bool isValid = false;
string text1 = base.GetControlValidationValue(base.ControlToValidate);
if ((text1 == null) || (text1.Trim().Length == 0))

{
isValid = false;
}
else

{
isValid = this.ValidateChar();
}

this.IsValid = isValid;
return this.IsValid;


}


/**//// <summary>
/// 查看是不是合法
/// </summary>
/// <returns></returns>
private bool ValidateChar()

{
string validatecode = ValidateCodeHandler.GetAndEmptyValidateCode(this.ValidateSign);
if (string.IsNullOrEmpty(validatecode))

{
return false;
}
else

{
return string.Compare(validatecode, this.GetControlValidationValue(this.ControlToValidate), this.IgnoreCase) == 0;
}
}

/**//// <summary>
/// 关联的图片控件ID
/// </summary>
[DescriptionAttribute("BaseValidator_ImageControl"), IDReferenceProperty, TypeConverter(typeof(ImageControlConverter)), Themeable(false), DefaultValue(""), CategoryAttribute("Behavior")]
public string ImageControl

{
get

{
object obj1 = this.ViewState["ImageControl"];
if (obj1 != null)

{
return (string)obj1;
}
return string.Empty;
}
set

{
this.ViewState["ImageControl"] = value;
}
}

protected System.Web.UI.WebControls.Image Image

{
get

{
if (string.IsNullOrEmpty(this.ImageControl))
throw new ArgumentNullException("ImageControl");

return this.NamingContainer.FindControl(this.ImageControl) as System.Web.UI.WebControls.Image;
}
}

protected string ValidateSign

{
get

{
if (ViewState["imagevalidate::validatesign"] == null)

{
ViewState["imagevalidate::validatesign"] = Utility.HashedString(string.Format("{0}::{1}",Page.Request.Url.AbsolutePath,this.UniqueID));
}

return Convert.ToString(ViewState["imagevalidate::validatesign"]);
}
}

protected override void OnPreRender(EventArgs e)

{
this.Image.ImageUrl = "#";
this.Image.Attributes["onload"] = "this.style.display=''";

if (this.EnableChangeImage)

{
Image.ToolTip = "看不清楚吗?双击换一张";
Image.Attributes["ondblclick"] = "if(!this.orgsrc)this.orgsrc=this.src;this.src=this.orgsrc + '&t=' + new Date().valueOf()";
}
this.Image.Style.Add(HtmlTextWriterStyle.Display, "none");
StringBuilder sbjs = new StringBuilder();
sbjs.AppendFormat("javascript:this.onerror=null;this.src='{0}?sign={1}';", Page.ResolveClientUrl(this.ValidateCodePage), this.ValidateSign);

this.Image.Attributes.Add("onerror", sbjs.ToString());

runtimeConfig[this.ValidateSign] = new ImageValidatorConfig(this.Chars, this.CharCount,this.FontColor,
this.Fonts,this.MinFontSize, this.MaxFontSize);

if (!this.IsValid)

{
ITextControl textInput = this.NamingContainer.FindControl(this.ControlToValidate) as ITextControl;
if (textInput != null)

{
textInput.Text = "";
}

}


base.OnPreRender(e);
}


}


/**//// <summary>
/// 查找Image控件ID列表
/// </summary>
public class ImageControlConverter : ControlIDConverter

{
protected override bool FilterControl(Control control)

{
if (control is System.Web.UI.WebControls.Image)
return true;
else

{
return false;
}
}
}


/**//// <summary>
/// 控制配置类
/// </summary>
public class ImageValidatorConfig

{
private string _chars = string.Empty;


/**//// <summary>
/// 可用字符列表构成的串
/// </summary>
/// <example>
/// 123456789
/// </example>
public string Chars

{
get

{
return _chars;
}

set

{
_chars = value;
}
}

private int _charCount = 5;


/**//// <summary>
/// 验证图片上字符数量
/// </summary>
public int CharCount

{
get

{
return _charCount;
}
set

{
_charCount = value;
}
}
private int _minFontSize = 60;


/**//// <summary>
/// 字体最小高度占Image控件高度的百分比
/// </summary>
public int MinFontSize

{
get

{
return _minFontSize;
}
set

{
_minFontSize = value;
}
}
private int _maxFontSize = 100;


/**//// <summary>
/// 字体最大高度占Image控件高度的百分比
/// </summary>
public int MaxFontSize

{
get

{
return _maxFontSize;
}
set

{
_maxFontSize = value;
}
}

private Color _fontColor = Color.Black;


/**//// <summary>
/// 字符颜色
/// </summary>
public Color FontColor

{
get

{
return _fontColor;
}
set

{
_fontColor = value;
}
}

private Color _bgColor = Color.White;


/**//// <summary>
/// 背景颜色
/// </summary>
public Color BgColor

{
get

{
return _bgColor;
}
set

{
_bgColor = value;
}
}

private string[] _fonts = null;


/**//// <summary>
/// 可用字体列表,生成时,会随机选择
/// </summary>
public string[] Fonts

{
get

{
return _fonts;
}
set

{
if (value == null)
throw new ArgumentNullException("Fonts");

if(value.Length == 0)

{
throw new ArgumentException("至少指定一种字体");
}
_fonts = value;
}
}

private int _maxRotate = 45;


/**//// <summary>
/// 每个字符最大旋转角度
/// </summary>
public int MaxRotate

{

get
{ return _maxRotate; }

set
{ _maxRotate = value; }
}




public ImageValidatorConfig(string chars, int charCount, Color fontColor, string[] fonts, int minFontSise, int maxFontSize)

{
this.Chars = chars;
this.CharCount = charCount;
this.FontColor = fontColor;
this.MinFontSize = minFontSise;
this.MaxFontSize = maxFontSize;
this.Fonts = fonts;
}
}


public class ValidateCodeHandler : System.Web.IHttpHandler, IRequiresSessionState

{

IHttpHandler 成员#region IHttpHandler 成员

public bool IsReusable

{

get
{ return false; }
}

public void ProcessRequest(HttpContext context)

{
try

{
string sign = context.Request.QueryString["sign"];

//生成一个验证码图片
using (System.Drawing.Image bmp = BuildValidateImage(sign))// images[sign];

{
//输出到浏览器
context.Response.Clear();
context.Response.ContentType = "image/Png";
bmp.Save(context.Response.OutputStream, ImageFormat.Jpeg);
context.Response.End();

}
}
catch

{
context.Response.End();

}
}

#endregion

protected static string SESSIONKEY = "iyond::imagevalidator";

protected static Dictionary<double, double> _cacheSqrt = new Dictionary<double, double>();


/**//// <summary>
/// 为了加快开方,自己写一个开方函数
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
public static double Sqrt(double d)

{
if (_cacheSqrt.ContainsKey(d))
return _cacheSqrt[d];
else

{
lock (_cacheSqrt)

{
double ret = Math.Sqrt(d);
_cacheSqrt[d] = ret;
return ret;
}
}
}


/**//// <summary>
/// 取验证码图片的字串形式,取出时清空。以防止漏洞
/// </summary>
/// <param name="sign">验证码配置标识</param>
/// <returns></returns>
public static string GetAndEmptyValidateCode(string sign)

{
if (ValidateCodeDictionary.ContainsKey(sign))

{
string validatecode = ValidateCodeDictionary[sign];
ValidateCodeDictionary[sign] = string.Empty;
return validatecode;
}
else

{
return string.Empty;
}
}



/**//// <summary>
/// 依配置生成验证码图片
/// </summary>
/// <param name="sign">验证码配置标识</param>
/// <returns></returns>
public static System.Drawing.Image BuildValidateImage(string sign)

{
//得到验证吗生成配置
ImageValidatorConfig config = ImageValidator.GetConfig(sign);

//生成新验证码
string validatecode = Utility.GetRandomChar(config.Chars, config.CharCount);
ValidateCodeDictionary[sign] = validatecode;

Random random = new Random();

//随机生成每个字要使用的字体和大小
List<DrawCharInfo> draws = new List<DrawCharInfo>();
using (Bitmap bmpTemp = new Bitmap(1, 1))

{
using (Graphics gm = Graphics.FromImage(bmpTemp))

{
using (SolidBrush brush = new SolidBrush(config.FontColor))

{
char[] chars = validatecode.ToCharArray();
foreach (char ch in chars)

{
string fontName = config.Fonts[random.Next(0, config.Fonts.Length - 1)];
float fontSize = Convert.ToSingle(random.Next(config.MinFontSize, config.MaxFontSize));
Font font = new Font(fontName, fontSize , FontStyle.Regular, GraphicsUnit.Pixel);

SizeF size = gm.MeasureString(ch.ToString(), font);

draws.Add(new DrawCharInfo(ch, font, size));
}
}
}
}

//找出最大高度,找出图片宽度
int bmpWidth = 0, bmpHeight = 0;
foreach (DrawCharInfo dci in draws)

{
bmpWidth += Convert.ToInt32(dci.Size.Width) + 1;
bmpHeight = Math.Max(bmpHeight, Convert.ToInt32(dci.Size.Height) + 1);
}


//依每个字符的配置,生成图片
Bitmap bmp = new Bitmap(bmpWidth, bmpHeight);
using (Graphics g = Graphics.FromImage(bmp))

{
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
using (SolidBrush brush = new SolidBrush(config.FontColor))

{
//用背景清空,将来可以考虑生成背景
g.Clear(config.BgColor);

//宽度计数
float x = 0;

foreach (DrawCharInfo dci in draws)

{
//保存场景配置
GraphicsState gs = g.Save();

//随机Y坐标
float y = random.Next(Math.Max(0, bmpHeight - Convert.ToInt32(dci.Size.Height))) / 2;

//随机旋转角度
float rotate = random.Next(-config.MaxRotate, config.MaxRotate);

//计算宽高
float halfWidth = dci.Size.Width / 2;
float halfHeight = dci.Size.Height / 2;

//变换坐标
g.TranslateTransform(x + halfWidth, y + halfHeight);
g.RotateTransform(rotate);

//打印字符
g.DrawString(dci.Char.ToString(), dci.Font, brush, -halfWidth, -halfHeight);

//原来场景
g.Restore(gs);

//增加宽度计数
x += dci.Size.Width;
}
}
}

//返回图片
return bmp;

}


/**//// <summary>
/// 保存每个标识的验证码字串
/// </summary>
protected static Dictionary<string, string> ValidateCodeDictionary

{
get

{
if (HttpContext.Current.Session[SESSIONKEY] == null)

{
HttpContext.Current.Session[SESSIONKEY] = new Dictionary<string, string>();
}

return HttpContext.Current.Session[SESSIONKEY] as Dictionary<string, string>;
}
}
}


/**//// <summary>
/// 每个字符的在验证吗图片的字体,大小信息
/// </summary>
internal class DrawCharInfo

{
private char _char;
private Font _font;
private SizeF _size;



/**//// <summary>
/// 字符
/// </summary>
public Char Char

{

get
{ return _char; }
}


/**//// <summary>
/// 字体
/// </summary>
public Font Font

{

get
{ return _font; }
}


/**//// <summary>
/// 字体所占区域
/// </summary>
public SizeF Size

{

get
{ return _size; }
}

public DrawCharInfo(char ch, Font font, SizeF size)

{
this._char = ch;
this._font = font;
this._size = size;
}
}
}
