2010年6月3日 星期四

圖形驗證(2): ASP.NET 簡易實作圖形驗證

接續上一篇的簡介,我們這一次實作一個簡易的 ASP.NET 的圖現驗證。

步驟1: 顯示圖形

一般來說,圖形只能給人工來閱讀,SpamRobot 碰到圖形當然較吃力。這個步驟,我們就先把文字變成最簡單的圖形吧。

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Web.UI;

namespace Captcha
{
  public partial class Sample1 : System.Web.UI.Page
  {
    protected void Page_Load(object sender, EventArgs e)
    {
      if (!Page.IsPostBack)
      {
        CreateImage("I wanna go home");
      }
      image.ImageUrl = ImageUrl;
    }

    //圖檔放置的位置
    private string ImagePath
    {
      get
      {
        return Server.MapPath(ImageUrl);
      }
    }
    //圖檔的 Url。使用 SessionID 避免重複
    private string ImageUrl
    {
      get
      {
        return "~/imgs/" + Session.SessionID + ".gif";
      }
    }

    private void CreateImage(string imageText)
    {
      //建立一個 Bitmap。寬高未定,故先給定1, 1
      Bitmap bmpImage = new Bitmap(1, 1);

      //指定字型
      Font font = new Font("Verdana", 24, FontStyle.Bold, GraphicsUnit.Point);

      //進行繪圖。繪圖需透過 Grahpics 物件。
      Graphics graphics = Graphics.FromImage(bmpImage);
      //測量文字的寬高
      int width = (int)graphics.MeasureString(imageText, font).Width;
      int height = (int)graphics.MeasureString(imageText, font).Height;

      //重新指定 Bitmap
      bmpImage = new Bitmap(bmpImage, new Size(width, height));

      //重新繪圖
      graphics = Graphics.FromImage(bmpImage);
      graphics.Clear(Color.White); //使用白底
      graphics.TextRenderingHint = TextRenderingHint.AntiAlias;  //不要鋸齒
      graphics.DrawString(imageText, font, new SolidBrush(Color.Red), 0, 0); //把文字畫上去
      graphics.Flush();

      bmpImage.Save(ImagePath, ImageFormat.Gif);

      //記得 release 記憶體
      graphics.Dispose();
      font.Dispose();
      bmpImage.Dispose();
    }
  }
}

而網頁使用了簡單的 asp:image

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Sample1.aspx.cs" Inherits="Captcha.Sample1" %>

<!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">
    <div>
      <asp:Image runat="server" ID="image" />
    </div>
    </form>
</body>
</html>

在步驟1中,我使用了 Session 作為圖檔的名稱,儲存在網站的目錄下,並以 asp:image 來顯示圖形。

 

步驟2: 亂數

上一個步驟中,驗證的文字是寫固定地。這個步驟我們就來亂一下吧!

 

protected void Page_Load(object sender, EventArgs e)
{
  if (!Page.IsPostBack)
  {
    string randomString = GetRandomString(4);
    CreateImage(randomString);
  }
  image.ImageUrl = ImageUrl;
}

private string GetRandomString(int length)
{
  char[] chars = @"2346789ABCDEFGHIJKLMNPQRTUVWXYZ".ToCharArray();
  Random r = new Random((int)DateTime.Now.Ticks);
  StringBuilder sb = new StringBuilder(length);
  for (int i = 0; i < length; i++)
    sb.Append(chars[r.Next(chars.Length)]);
  return sb.ToString();
}

顯示的文字,不能讓使用者不小心看錯,例如O 與 0,l, i, 與 1,S與5。為了易於使用,必須將這些容易誤解的字元全部移掉。圖形如下

image

步驟3: 驗證

這是最簡單的步驟了。將驗證用的文字儲存起來,放到 Session中,等待使用者的回應並檢查之。

protected void Page_Load(object sender, EventArgs e)
{
  if (!Page.IsPostBack)
  {
    VerifyNow();
  }
}

private void VerifyNow()
{
  string randomString = GetRandomString(4);
  Session["ImgText"] = randomString;
  CreateImage(randomString);
  image.ImageUrl = ImageUrl;
}

protected void btnSubmit_Click(object sender, EventArgs e)
{
  string randomString = Session["ImgText"] as string;
  if (randomString != txtAnswer.Text.Trim())
  {
    Response.Write("驗證錯誤");
    VerifyNow(); //重新產生驗證文字
  }
  else
    Response.Write("驗證成功");
}

結論

這是一個極簡單的CAPTCHA圖形驗證的範例。雖然已經可以運作了,但仍然有不少的缺點,例如可重用性,使用了Session,以及最重要的是「圖形太簡單」的問題。

由於道高一尺魔高一丈的布袋戲真理,一定有 AntiCaptcha 工具的產生,圖形太過於簡單,等同於不設防。另外驗證文字的長度也太短了些,10000次總有一次會猜中,也是個問題。

範例可在這裡下載

沒有留言:

Share with Facebook