接續上一篇的簡介,我們這一次實作一個簡易的 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次總有一次會猜中,也是個問題。
範例可在這裡下載
沒有留言:
張貼留言