[ASP.net WebForm] 圖形驗證碼(C#) + 前端驗證 - 懶人Code

  • 44597
  • 0
  • AJAX
  • 2012-09-17

[ASP.net WebForm] 圖形驗證碼(C#) + 前端驗證 - 懶人Code

先在網站專案中新增一個泛型處理常式 ValidateNumber.ashx

並加入以下的命名空間


using System;
using System.Web;
using System.Drawing;
using System.Web.SessionState;

 

完整產生圖形驗證碼(只有數字)的Code


<%@ WebHandler Language="C#" Class="ValidateNumber" %>

using System;
using System.Web;
using System.Drawing;
//如何於泛型處理常式 .ashx 中存取工作階段變數(Session Variable) 
using System.Web.SessionState;
//http://www.limingchstudio.com/2009/05/ashx-session-variable.html


/*2011.10.5  Shadow 改寫,其他程式要判斷是否驗證成功,請比較Session["ValidateNumber"]的字串*/
public class ValidateNumber : IHttpHandler, IRequiresSessionState
{
    
    public void ProcessRequest (HttpContext context) {
        
        int NumCount = 5;//預設產生5位亂數
        if(!string.IsNullOrEmpty(context.Request.QueryString["NumCount"]))
        {//有指定產生幾位數
           
            //字串轉數字,轉型成功的話儲存到 NumCount,不成功的話,NumCount會是0
          Int32.TryParse(context.Request.QueryString["NumCount"].Replace("'","''"),out NumCount);
        }
        if(NumCount==0)NumCount=5;
        //取得亂數
        string str_ValidateCode = this.GetRandomNumberString(NumCount);
        /*用於驗證的Session*/
        context.Session["ValidateNumber"] = str_ValidateCode;
        
        //取得圖片物件
        System.Drawing.Image image = this.CreateCheckCodeImage(context,str_ValidateCode);
        System.IO.MemoryStream ms = new System.IO.MemoryStream();
        image.Save(ms , System.Drawing.Imaging.ImageFormat.Jpeg);
        /*輸出圖片*/
        context.Response.Clear();
        context.Response.ContentType = "image/jpeg";
        context.Response.BinaryWrite(ms.ToArray());
        ms.Close();
            
    }
 
    #region 產生數字亂數
    private string GetRandomNumberString(int int_NumberLength)  
    {
        System.Text.StringBuilder str_Number = new System.Text.StringBuilder();//字串儲存器
        Random rand = new Random(Guid.NewGuid().GetHashCode());//亂數物件
        
        for(int i = 1 ;i<= int_NumberLength ;i++)
        {
            str_Number.Append( rand.Next(0,10).ToString() );//產生0~9的亂數
        }
        
        return str_Number.ToString();
    }
    #endregion

    #region 產生圖片
    private System.Drawing.Image CreateCheckCodeImage(HttpContext context,string checkCode)
    {

        System.Drawing.Bitmap image = new System.Drawing.Bitmap((checkCode.Length * 20), 40);//產生圖片,寬20*位數,高40像素
        System.Drawing.Graphics g =   Graphics.FromImage(image);

        
            //生成隨機生成器
            Random random =new Random(Guid.NewGuid().GetHashCode());
            int int_Red =0;
            int int_Green =0;
            int int_Blue =0;
            int_Red = random.Next(256);//產生0~255
            int_Green = random.Next(256);//產生0~255
            int_Blue = (int_Red + int_Green > 400 ?  0 : 400 - int_Red - int_Green);
            int_Blue = (int_Blue > 255 ? 255: int_Blue);

            //清空圖片背景色
            g.Clear(Color.FromArgb(int_Red, int_Green, int_Blue));
        
            //畫圖片的背景噪音線
            for( int i  = 0;i<= 24;i++)
            {

                
                int x1   = random.Next(image.Width);
                int x2   = random.Next(image.Width);
                int y1   = random.Next(image.Height);
                int y2   = random.Next(image.Height);

                g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
              
                g.DrawEllipse(new Pen(Color.DarkViolet), new System.Drawing.Rectangle(x1, y1, x2, y2));
            }

            Font font = new System.Drawing.Font("Arial", 20, (System.Drawing.FontStyle.Bold));
            System.Drawing.Drawing2D.LinearGradientBrush brush = new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), Color.Blue, Color.DarkRed, 1.2F, true);
          
            g.DrawString(checkCode, font, brush, 2, 2);
            for(int i = 0 ;i<= 99;i++)
            {

                //畫圖片的前景噪音點
                int x   = random.Next(image.Width);
                int y   = random.Next(image.Height);

                image.SetPixel(x, y, Color.FromArgb(random.Next()));
            }

            //畫圖片的邊框線
            g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
             
            
            return image;
     
    }
    #endregion

    /*實作 IHttpHandler介面的方法,不可刪除*/
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }


}

 

 

使用方式:


<%@ 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></title>
</head>
<body>
    <form id="form1" runat="server">
     
     <!--src連結到ValidateNumber.ashx即可-->
     <img src="ValidateNumber.ashx" alt="驗證碼" />

    </form>
</body>
</html>

 

如果要加上一個重新整理圖片(不postback)按鈕的話(參考來源:How to refresh the src of <img /> with jQuery?)


    <form id="form1" runat="server">
     
     <!--src連結到ValidateNumber.ashx即可-->
     <img src="ValidateNumber.ashx" alt="驗證碼" name="imgCode" /> 
     <input type="button" onclick="form1.imgCode.src='ValidateNumber.ashx?' + Math.random();" value="重新整理" />

    </form>

 

 

 

接下來再做一個前端驗證,畫面大概這樣


<%@ 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></title>
    <script src="js/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script type="text/javascript">

        function isPassValidateCode() 
        { 
          
        
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
     
     <!--src連結到ValidateNumber.ashx即可-->
     <img src="ValidateNumber.ashx" alt="驗證碼" name="imgCode" /> 
     <input type="button" onclick="form1.imgCode.src='ValidateNumber.ashx?' + Math.random();" value="重新整理" />
     <hr />


     <!--前端驗證結果訊息要放到span_result的innerHtml-->
     <asp:TextBox ID="txt_input" runat="server" /><span id="span_result"></span>
     <asp:Button Text="送出" ID="btn_submit" runat="server" OnClientClick="return isPassValidateCode();" />
    </form>
</body>
</html>

 

 

再新增一個 readSessionValidateNumber.ashx專門做讀取Session[“ValidateNumber”]動作


<%@ WebHandler Language="C#" Class="readSessionValidateNumber" %>

using System;
using System.Web;
//如何於泛型處理常式 .ashx 中存取工作階段變數(Session Variable) 
using System.Web.SessionState;
//參考:http://www.limingchstudio.com/2009/05/ashx-session-variable.html

public class readSessionValidateNumber : IHttpHandler, IRequiresSessionState
{
    
    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        string ValidateNumber = (string)context.Session["ValidateNumber"];
        context.Response.Write(ValidateNumber);
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }

}

然後用jQuery Ajax改寫function isPassValidateCode()


    <script type="text/javascript">

        function isPassValidateCode() {
          var  nowValidateNumber =  jQuery.ajax({
                url: "readSessionValidateNumber.ashx",
                type: "post",
                async: false,
                data:{},
                success: function (htmlVal) {  }
            }).responseText;

            var userInput = jQuery("#<%= txt_input.ClientID%>").val();

            var validateResult = ((nowValidateNumber == userInput) ? true : false);


            if (validateResult == false) {
                jQuery("#span_result").html("驗證碼輸入不正確");
            }

            //回傳true Or false
            return validateResult;
        }
    </script>

 

另外在此支WebForm的Code-Behind寫下送出的Click事件,觀察看輸入的正不正確


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

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

    }
    protected void btn_submit_Click(object sender, EventArgs e)
    {
        Response.Write("剛剛輸入的是" + txt_input.Text + "<hr/>");
    }
}

完整的 .aspx Code


<%@ 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></title>
    <script src="js/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script type="text/javascript">

        function isPassValidateCode() {
          var  nowValidateNumber =  jQuery.ajax({
                url: "readSessionValidateNumber.ashx",
                type: "post",
                async: false,
                data:{},
                success: function (htmlVal) {  }
            }).responseText;

            var userInput = jQuery("#<%= txt_input.ClientID%>").val();

            var validateResult = ((nowValidateNumber == userInput) ? true : false);


            if (validateResult == false) {
                jQuery("#span_result").html("驗證碼輸入不正確");
            }

            //回傳true Or false
            return validateResult;
        }
    </script>
    <!--驗證結果訊息為了美觀,多追加以下的css-->
    <style type="text/css">
    #span_result
    {
     color:Red;
     font-size:12px;      
     }
    </style>
</head>
<body>
    <form id="form1" runat="server">
     
     <!--src連結到ValidateNumber.ashx即可-->
     <img src="ValidateNumber.ashx" alt="驗證碼" name="imgCode" /> 
     <input type="button" onclick="form1.imgCode.src='ValidateNumber.ashx?' + Math.random();" value="重新整理" />
     <hr />


     <!--前端驗證結果訊息要放到span_result的innerHtml-->
     <asp:TextBox ID="txt_input" runat="server" /><span id="span_result"></span>
     <br />
     <asp:Button Text="送出" ID="btn_submit" runat="server" 
         OnClientClick="return isPassValidateCode();" onclick="btn_submit_Click" />
    </form>
</body>
</html>

執行畫面:

image

按下「重新整理」(網頁不會全部刷新)

image

未輸入或輸入錯誤的字串然後按送出

imageimage

輸入正確的數字按送出的話

image  →   image

 

 

 

 

 

 

 

這程式應該還可以有改進空間,當jQuery Ajax取得Session中字串時,若Session逾時,應該要顯示逾時訊息給使用者

告訴使用者請再按重新整理,無奈我要下班了,所以這部份改天研究。

 

2011.10.5 18:50追加

當Session逾時,通知使用者的javascript code


    <script type="text/javascript">

        function isPassValidateCode() {
          var  nowValidateNumber =  jQuery.ajax({
                url: "readSessionValidateNumber.ashx",
                type: "post",
                async: false,
                data:{},
                success: function (htmlVal) {  }
            }).responseText;

            if (nowValidateNumber == "" || nowValidateNumber == null) {
                alert("驗證碼逾時,請重新整理");
                return false;
            }
            var userInput = jQuery("#<%= txt_input.ClientID%>").val();

            var validateResult = ((nowValidateNumber == userInput) ? true : false);


            if (validateResult == false) {
                jQuery("#span_result").html("驗證碼輸入不正確");
            }

            //回傳Ctrue Or false
            return validateResult;
        }
    </script>

 

試了一下,當Session逾時,按了重新整理按鈕後,若成功送出postback時,圖片仍會和第一張一樣

但Session裡的字串早已不一樣,使用者就算輸入和圖片一樣的數字,仍然不會通過驗證

為了避免此Bug,javascript要再改寫:(↓全部完整的 .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></title>
    <script src="js/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        jQuery(document).ready(init);
        function init() {

           /*每次Dom載入完,確保圖片都不一樣*/
           jQuery("img[name='imgCode']").attr("src", "ValidateNumber.ashx?" + Math.random());
        
        }

        function isPassValidateCode() {
          var  nowValidateNumber =  jQuery.ajax({
                url: "readSessionValidateNumber.ashx",
                type: "post",
                async: false,
                data:{},
                success: function (htmlVal) {  }
            }).responseText;

            if (nowValidateNumber == "" || nowValidateNumber == null) {
                alert("驗證碼逾時,請重新整理");
                return false;
            }
            var userInput = jQuery("#<%= txt_input.ClientID%>").val();

            var validateResult = ((nowValidateNumber == userInput) ? true : false);


            if (validateResult == false) {
                jQuery("#span_result").html("驗證碼輸入不正確");
            }

            //回傳true Or false
            return validateResult;
        }
    </script>
    <!--驗證結果訊息為了美觀,多追加以下的css-->
    <style type="text/css">
    #span_result
    {
     color:Red;
     font-size:12px;      
     }
    </style>
</head>
<body>
    <form id="form1" runat="server">
     
     <!--src連結到ValidateNumber.ashx即可-->
     <img src="ValidateNumber.ashx" alt="驗證碼" name="imgCode"  /> 
     <input type="button" onclick="form1.imgCode.src='ValidateNumber.ashx?' + Math.random();" value="重新整理" />
     <hr />


     <!--前端驗證結果訊息要放到ispan_result的innerHtml-->
     <asp:TextBox ID="txt_input" runat="server" /><span id="span_result"></span>
     <br />
     <asp:Button Text="送出" ID="btn_submit" runat="server" 
         OnClientClick="return isPassValidateCode();" onclick="btn_submit_Click" />
    </form>
</body>
</html>

 

完整範例程式檔下載

 

 

2011.11.25 公司ART人員發現的簡潔樣式
http://www.tipstricks.org/
(下方可以Download Sample Code)

2012.01.07 產生的驗證碼圖片不可以存成實體文件,否則會有Cache問題

用AJAX提交数据获取验证码的问题