接續上一篇的簡介,我們這一次實作一個簡易的 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。為了易於使用,必須將這些容易誤解的字元全部移掉。圖形如下
步驟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次總有一次會猜中,也是個問題。
範例可在這裡下載
 
 
沒有留言:
張貼留言