c# 图形验证码的生成和验证

来源:互联网 发布:宿迁12345网络问政一把 编辑:程序博客网 时间:2024/05/12 23:03

看到网络上的图形验证码之后想到以后做网站可能都会用到,所以就从网上搜了一点资料整理了一下,另外因为把验证码存到session里也是不安全的,像csdn 的图形验证码就是存到session里的 所以我用了另外一种存储方式,利用txt文件临时存储这些验证信息,但是要单独指定存放txt 和img的文件夹 图形是png格式的

源代码里的许多方法都是参考网上的源码,作者就很难考究了,但是还是谢谢他们。。。

GraphicAuthentation 类 要增加对System.Drawing 和 System.Configuration 程序集的引用

 

using System;
using System.IO;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;


/// <summary>
/// A class to generate authentation image and check if the string input is the same as the text in the image
/// To use this class you may set an safe folder to store the txt files containing the authentation info,and set this path in
/// webconfig appsettings key:"AuthentationTxtFolderPath" value is the physical path and when you want to save the image you should 
/// give the image folder path such as  Server.MapPath("~/Tem/authentationImgFolder") make sure all the folders above is for authentation
/// only because this class will delete files in the foldes you set 
/// 
/// problems shoot:can't find the image generated :make sure the fullAuthentationImage folder is correct before call the function,because it will 
/// create folder if the path dosen't exist this may cause problem
/// the image format is png so make sure to use the correct extension
/// 
/// many methods is written by others  thanks to the author
/// 
/// @Author ChenGuofeng
/// @Version:1.0
/// @Email : Guofeng31@gmail.com
/// This code is open source you can use it freely but please keep the text above 
/// </summary>

public class GraphicAuthentation
{


    
public GraphicAuthentation()
    
{
        
this.random = new Random();
        
this.fontFamilyName = "Arial";
        
this.textLength = 4;
        
this.fontSize = 16;
        
this.imageHeight = 25;
        
this.textFont = new Font(fontFamilyName, fontSize, FontStyle.Strikeout);  //验证码字体 
        this.charCollection = "2,3,4,5,6,7,8,9,a,s,d,f,g,h,z,c,v,b,n,m,k,q,w,e,r,t,y,u,p"//定义验证码字符及出现频次 ,避免出现0 o j i l 1 x;  
        this.pointNum = 8;
        
this.lineNum = 2;
        
this.backGroundBrush = new SolidBrush(GetRandomLightColor());
        
this.textBrush = new SolidBrush(GetRandomDeepColor());
        
this.interferenceBrush = new SolidBrush(GetRandomLightColor());
        
this.randomString = GetRandomString();
    }

    
public GraphicAuthentation(Font textFont, int imageWidth, int imageHeight)
        : 
this()
    
{
        
if (null != textFont && imageWidth > 0 && imageHeight > 0)
        
{
            
this.textFont = textFont;
            
this.imageWidth = imageWidth;
            
this.imageHeight = imageHeight;
        }

        
else
        
{
            
throw (new ArgumentException("Ilegal arguments"));
        }

    }

    
properties
    
/// <summary>
    
/// Generate a image and save the random string to a txt file
    
/// </summary>
    
/// <returns></returns>

    public System.Drawing.Bitmap GenerateImage()
    
{

        
try
        
{

            
//如果为定义宽度,则自动调整
            if (imageWidth == 0)
            
{
                imageWidth 
= Convert.ToInt32(randomString.Length) * fontSize + fontSize;  //验证区宽度,加宽一个字符,为变形预留空间    
            }


            
this.image = new System.Drawing.Bitmap(imageWidth, imageHeight); //验证区高度    


            
//在图片框上面建立一个新的空白Graphics    
            this.graphics = Graphics.FromImage(image);

            
//填充位图背景    
            FillBackGround();

            
//画随机躁点        
            DrawRandomPoint();

            
//画随机噪音线
            DrawRandomLine();

            
//画随机文字,并生成随机3D背景

            DrawRandomString();

            
//随机扭曲图片
            RandomTwistImage();

            
//存储图片和验证码信息
            SaveImageInfo();

            
//清理过时的txt文档,img 文件在存储新的img 时进行清理
            CleanObsoleteTxtFiles();

            
return this.image;
        }

        
catch
        
{
            
this.image = null;
            
return this.image;
        }

    }

    
/// <summary>
    
/// Save the random string to a txt file ,the "AuthentationTxtFolder" can be set in the webconfig 
    
/// or the programe will create an folder automaticly the txt file has the same name with the image
    
/// </summary>

    private void SaveImageInfo()
    
{

        
string authentationTxtFolderPath = GetAuthentationTxtFolderPath();

        
this.imageName = DateTime.Now.Ticks.ToString();                                       //根据系统时间生成文件名称
        this.txtFileName = GetTxtNameFromImageName(ref this.imageName);
        
if (string.IsNullOrEmpty(authentationTxtFolderPath))                                  //如果没有在webconfig中配置则在当前盘的根目录下建立AuthentationTxtFolder,此文件夹应禁止浏览者访问
        {
            authentationTxtFolderPath 
= Path.GetFullPath("/"+ "AuthentationTxtFolder/";
        }

        
if (!Directory.Exists(authentationTxtFolderPath))
        
{
            Directory.CreateDirectory(authentationTxtFolderPath);
        }

        StreamWriter imageInfo 
= new StreamWriter(authentationTxtFolderPath + this.txtFileName);


        imageInfo.WriteLine(
this.randomString);
        imageInfo.Close();

    }

    
/// <summary>
    
/// Get the authentation image and save the authentation text to a txt file
    
/// </summary>
    
/// <param name="fullImageFolderPath">The full path of the imagefolder such as"C:/AuthentationImageFolder/" make sure the folder is only for the authentation image for not deleting other files 
    
/// when clean obsolete files</param>
    
/// <returns>if succeed return true,else return false</returns>

    public bool GenerateAndSaveImage(string fullImageFolderPath)
    
{
        
bool result = false;
        
// string imageExtention = ".png";
        if (fullImageFolderPath.LastIndexOf("/"< fullImageFolderPath.Length - 1)  //判断路径是否以"/"结尾,不是则自动补加
        {
            fullImageFolderPath 
+= "/";
        }

        
try
        
{
            
this.image = GenerateImage();
            
if (null != this.image && !string.IsNullOrEmpty(this.imageName))
            
{
                
if (!Directory.Exists(fullImageFolderPath))
                
{
                    Directory.CreateDirectory(fullImageFolderPath);
                }

                
this.image.Save(fullImageFolderPath + this.imageName, ImageFormat.Png);
                result 
= true;


                
//清理过时的图像文件
                CleanObsoleteImageFiles(fullImageFolderPath);
            }

            
else { }
        }

        
catch
        
{
        }

        
return result;
    }


    
/// <summary>
    
/// Check the  string to a txt file, the "AuthentationTxtFolderPath" can be set in the webconfig 
    
/// or the programe will create an folder automaticly
    
/// </summary>
    
/// <param name="imageName"></param>
    
/// <param name="textToCheck"></param>
    
/// <returns></returns>

    public bool CheckTextInImage(string imageName, string textToCheck)
    
{
        
bool result = false;
        imageName 
= imageName.ToLower();   //不区分大小写 
        string authentationTxtFolderPath = GetAuthentationTxtFolderPath();
        
string txtName = GetTxtNameFromImageName(ref imageName);


        StreamReader streamReader 
= new StreamReader(authentationTxtFolderPath + txtName);
        
if (null != streamReader)
        
{
            
string textInTxtFile = streamReader.ReadLine().Trim();
            
if (textInTxtFile == textToCheck)
            
{
                streamReader.Close();
                result 
= true;
            }

        }

        
return result;
    }

    
public bool CheckTextAndDelete(string imageName, string textToCheck, string fullImageFolderPath)
    
{
        
if (CheckTextInImage(imageName, textToCheck) && DeleteInfo(fullImageFolderPath, imageName))
            
return true;
        
else
            
return false;
    }


    
public bool DeleteInfo(string fullImageFolderPath, string imageName)
    
{
        
bool result = false;
        
string authentationTxtFolderPath = GetAuthentationTxtFolderPath();   //txt file has the same name with the image
        string txtName = GetTxtNameFromImageName(ref imageName);
        
if (fullImageFolderPath.LastIndexOf("/"< fullImageFolderPath.Length - 1)  //判断路径是否以"/"结尾,不是则自动补加
        {
            fullImageFolderPath 
+= "/";
        }


        
try
        
{
            File.Delete(authentationTxtFolderPath 
+ txtName); //确保扩展名正确
            File.Delete(fullImageFolderPath + imageName);
            result 
= true;
        }

        
catch
        
{ }
        
return result;

    }

    
/// <summary>
    
/// Get TxtfileName from the image name and change image name when it is without extention
    
/// </summary>
    
/// <param name="imageName"></param>
    
/// <returns></returns>

    private string GetTxtNameFromImageName(ref string imageName)
    
{
        
string txtName = "";
        
if (imageName.IndexOf(".png"> 0)
            txtName 
= imageName.Substring(0, imageName.IndexOf(".png")) + ".txt";
        
else
        
{
            
if (imageName.IndexOf("."> 0//The extention is incorrect
            {
                
throw new Exception("图像的扩展名为.png");
            }

            txtName 
= imageName + ".txt";
            imageName 
+= ".png";
        }

        
return txtName;
    }


    
/// <summary>
    
/// Get the directory used to save the txt file if not exits, the folder will be created
    
/// the path can be set in the webconfig key :"AuthentationTxtFolderPath"
    
/// </summary>
    
/// <returns></returns>

    private string GetAuthentationTxtFolderPath()
    
{
        
string authentationTxtFolderPath = System.Configuration.ConfigurationManager.AppSettings["AuthentationTxtFolderPath"];

        
if (string.IsNullOrEmpty(authentationTxtFolderPath))                                  //如果没有在webconfig中配置则在当前盘的根目录下建立AuthentationTxtFolder,此文件夹应禁止浏览者访问
        {
            authentationTxtFolderPath 
= Path.GetFullPath("/"+ "AuthentationTxtFolder/";
        }

        
if (!Directory.Exists(authentationTxtFolderPath))
        
{
            Directory.CreateDirectory(authentationTxtFolderPath);
        }

        
return authentationTxtFolderPath;
    }

    
private void FillBackGround()
    
{
        
if (null == this.backGroundBrush)
        
{
            
this.backGroundBrush = new SolidBrush(GetRandomLightColor());
        }

        
this.graphics.FillRectangle(backGroundBrush, 00, image.Width, image.Height);
    }


    
/// <summary>
    
///将全局变换平移(x, y),也就是使画布上将要画的所有内容向左边移动x,向下移动y (not used)  
    
/// </summary>
    
/// <param name="graphics"></param>

    private void TransformGraphics(Graphics graphics)
    
{


        
float left = -15;
        
float top = -2;
        graphics.TranslateTransform(left, top);

        
//做切变,将原始矩形的下边缘水平移动矩形高度的0.2倍    
        Matrix textTransform = graphics.Transform;
        textTransform.Shear(
0.2f0);
        graphics.Transform 
= textTransform;

    }


    
/// <summary>
    
/// Get a random string from the collection of chars     
    
/// </summary>
    
/// <returns></returns>

    private string GetRandomString()
    
{
        
string[] randomArray = charCollection.Split(','); //将字符串生成数组     
        int arrayLength = randomArray.Length;
        
string randomString = "";
        
for (int i = 0; i < this.textLength; i++)
        
{
            randomString 
+= randomArray[random.Next(0, arrayLength)];
        }

        
return randomString; //长度是textLength +1
    }




    
/// <summary>
    
/// 正弦曲线Wave扭曲图片。函数可以迭加使用,以获得不同方式不同程度的效果
    
/// </summary>
    
/// <param name="srcBmp">Bitmap to modify</param>
    
/// <param name="bXDir">扭曲方式,true 垂直,false 水平 </param>
    
/// <param name="dMultValue">波形的幅度倍数</param>
    
/// <param name="dPhase">波形的起始相位</param>
    
/// <returns></returns>

    public Bitmap TwistImage(Bitmap srcBmp, bool bXDir, double dMultValue, double dPhase)
    
{
        
int leftMargin = 0;
        
int rightMargin = 0;
        
int topMargin = 0;
        
int bottomMargin = 0;
        
//float PI = 3.14159265358979f;
        float PI2 = 6.28318530717959f;
        Bitmap destBmp 
= new Bitmap(srcBmp.Width, srcBmp.Height);
        
double dBaseAxisLen = bXDir ? Convert.ToDouble(destBmp.Height) : Convert.ToDouble(destBmp.Width);
        
for (int i = 0; i < destBmp.Width; i++)
        
{
            
for (int j = 0; j < destBmp.Height; j++)
            
{
                
double dx = 0;
                dx 
= bXDir ? PI2 * Convert.ToDouble(j) / dBaseAxisLen : PI2 * Convert.ToDouble(i) / dBaseAxisLen;
                dx 
+= dPhase;
                
double dy = Math.Sin(dx);

                
//取得当前点的颜色    
                int nOldX = 0;
                
int nOldY = 0;
                nOldX 
= bXDir ? i + Convert.ToInt32(dy * dMultValue) : i;
                nOldY 
= bXDir ? j : j + Convert.ToInt32(dy * dMultValue);
                System.Drawing.Color color 
= srcBmp.GetPixel(i, j);
                
if (nOldX >= leftMargin && nOldX < destBmp.Width - rightMargin && nOldY >= bottomMargin && nOldY < destBmp.Height - topMargin)
                
{
                    destBmp.SetPixel(nOldX, nOldY, color);
                }

            }

        }

        
return destBmp;
    }


    
/// <summary>
    
/// 生成随机浅颜色
    
/// </summary>
    
/// <returns>randomColor</returns>


    
public Color GetRandomLightColor()
    
{
        
int nRed, nGreen, nBlue;    //越大颜色越浅
        int low = 180;           //色彩的下限
        int high = 255;          //色彩的上限      
        nRed = this.random.Next(high) % (high - low) + low;
        nGreen 
= this.random.Next(high) % (high - low) + low;
        nBlue 
= this.random.Next(high) % (high - low) + low;
        Color color 
= Color.FromArgb(nRed, nGreen, nBlue);
        
return color;
    }

    
/// <summary>
    
/// 生成随机深颜色
    
/// </summary>
    
/// <returns></returns>

    public Color GetRandomDeepColor()
    
{
        
int nRed, nGreen, nBlue;    // nBlue,nRed  nGreen 相差大一点 nGreen 小一些
        
//int high = 255;       
        int redLow = 120;
        
int greenLow = 0;
        
int blueLow = 120;
        nRed 
= this.random.Next(redLow);
        nGreen 
= this.random.Next(greenLow);
        nBlue 
= this.random.Next(blueLow);
        Color color 
= Color.FromArgb(nRed, nGreen, nBlue);
        
return color;
    }

    
/// <summary>
    
/// 随机输出躁点
    
/// </summary>
    
/// <param name="penWidth">The width of the point</param>
    
/// <returns></returns>

    private void DrawRandomPoint()
    
{
        
float x, y;
        
for (int i = 0; i < this.pointNum; i++)
        
{
            x 
= imageWidth * (float)this.random.NextDouble();
            y 
= imageHeight * (float)this.random.NextDouble();
            
float pointWidth = 2 * (float)this.random.NextDouble(); //随机干扰点大小
            graphics.DrawRectangle(new Pen(GetRandomLightColor(), pointWidth), x, y, 11);//draw a rectangle on the graphic
        }


    }


    
private void DrawRandomLine()
    
{
        
float x1, x2, y1, y2;
        
for (; this.lineNum > 0this.lineNum--)
        
{
            x1 
= image.Width * (float)this.random.NextDouble();
            y1 
= image.Height * (float)this.random.NextDouble();
            x2 
= image.Width * (float)this.random.NextDouble();
            y2 
= image.Height * (float)this.random.NextDouble();
            
float penWidth = 2 * (float)this.random.NextDouble(); //随机干扰线粗细
            this.graphics.DrawLine(new Pen(GetRandomLightColor(), penWidth), x1, y1, x2, y2);
        }


    }

    
/// <summary>
    
/// 写入字符串,并随机生成3D背景
    
/// </summary>

    private void DrawRandomString()
    
{
        
//文字的位置         
        float x = 10 * (float)this.random.NextDouble() + 4//随机产生X轴位置,增加程序识别难度    
        float y = 0;
        
double draw3DProbability = 0.9;               //生成3D背景的概率
        int distance = Convert.ToInt32(random.NextDouble() * 2);//3D背景偏移的距离
        this.graphics.DrawString(this.randomString, this.textFont, this.textBrush, x, y);
        
if (!(random.NextDouble() > draw3DProbability))  //生成3D背景
        {

            
this.graphics.DrawString(this.randomString, this.textFont, this.interferenceBrush, x - distance, y + distance);
        }

    }


    
private void RandomTwistImage()
    
{
        
//扭曲验证字符。TwistImage参数可自行修改    
        int twist1, twist2;
        
double twistProbability = 0.1;//产生扭曲的概率
        int twistLeval = 2;
        
if (this.random.NextDouble() > twistProbability)
        
{
            twist1 
= 0;
            twist2 
= 0;
        }

        
else
        
{
            twist1 
= Convert.ToInt32(this.random.NextDouble() * twistLeval);  //扭曲参数随机生成    
            twist2 = Convert.ToInt32(this.random.NextDouble() * twistLeval - 1); //扭曲参数随机生成    
        }


        
//image = TwistImage(image, true, -twist1, -twist2);
        image = TwistImage(image, false-twist1, -twist2); //多扭曲几次也没关系,只是消耗服务器资源多些  
    }

    
/// <summary>
    
/// 如果没有显式的删除文件,临时的验证文件会越来越多,此方法用来清除过时的文件,当文件的创建时间小于服务器时间两小时以上时则被删除
    
/// 
    
/// </summary>

    public void CleanObsoleteImageFiles(string fullImageFolderPath)
    
{
        
string[] fileCollections = Directory.GetFiles(fullImageFolderPath);
        
int filesNum = fileCollections.Length;
        
for (int i = 0; i < filesNum; i++)
        
{
            ApplyDeletFile(fileCollections[i]);
        }

    }

    
/// <summary>
    
/// 如果不存储Image 则单独进行txt文件的清理
    
/// </summary>

    private void CleanObsoleteTxtFiles()
    
{
        
string[] fileCollections = Directory.GetFiles(GetAuthentationTxtFolderPath());
        
int filesNum = fileCollections.Length;
        
for (int i = 0; i < filesNum; i++)
        
{
            ApplyDeletFile(fileCollections[i]);
        }

    }

    
private void ApplyDeletFile(string fullFileName)
    
{
        
double timeSpan = 2;
        
try
        
{
            DateTime createTime 
= File.GetCreationTime(fullFileName);
            
if (createTime.AddHours(timeSpan) < DateTime.Now)
                File.Delete(fullFileName);
        }

        
catch { }
    }


}




 

default.aspx

 

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    
<title>Untitled Page</title>
</head>
<body>
    
<form id="Form1" method="post" runat="server">   
            
<div>   
                
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>   
                
<asp:ImageButton ID="Image1" runat="server" ImageUrl="GetAuthentationImage.aspx" /><br>  
                
<img runat="server" id="img2" src="GetAuthentationImage.aspx" onclick = "javascript:this.src ='GetAuthentationImage.aspx'" /> 
                
<br>   
                
<asp:Button ID="Button1" runat="server" Text="提交" OnClick="Button1_Click" />   
            
</div>   
        
</form>  
</body>
</html>

 

default.aspx.cs

 

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class _Default : System.Web.UI.Page 
{
    
private void Page_Load(object sender, System.EventArgs e)
    
{
      
       
    }


    
private void Image1_Click(object sender, System.Web.UI.ImageClickEventArgs e)
    
{
        Image1.ImageUrl 
= "GetAuthentationImage.aspx";
    }


    
protected void Button1_Click(object sender, EventArgs e)
    
{
        GraphicAuthentation g 
= new GraphicAuthentation();
        
if (g.CheckTextInImage(Session["imageName"].ToString(), Request.Form["textBox1"]))
        
{
            Response.Write(
"Success");          
        }

        
else
        
{
            Response.Write(
"fail");
        }

    }

}

    

 

GetAuthentationImage.aspx.cs

using System;
using System.IO;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

public partial class GetAuthentationImage : System.Web.UI.Page
{


    
private void Page_Load(object sender, System.EventArgs e)
    
{

        Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache); 
//不缓存  
        GraphicAuthentation graphicAuthentation = new GraphicAuthentation();
        Bitmap image 
= new Bitmap(7025);
        
string imageName = "";
        
string path = Server.MapPath("~/Tem/AuthentationImageFolder");
        
if (graphicAuthentation.GenerateAndSaveImage(path))
        
{
            imageName 
= graphicAuthentation.ImageName;
            image 
= graphicAuthentation.Image;
        }

        Session[
"imageName"= imageName;
        
//string name = graphicAuthentation.GetAuthentationImageUrl();
        MemoryStream ms = new MemoryStream();
        image.Save(ms, ImageFormat.Png);
        Response.ClearContent();    
//需要输出图象信息 要修改HTTP头     
        Response.ContentType = "image/Png";
        Response.BinaryWrite(ms.ToArray());
        image.Dispose();
        Response.End();
    }


}

目录结构

目录结构

效果

       效果

 

终于贴完了 呵呵 ~~

原创粉丝点击