﻿# 如何用 ASP.NET 實做簡單的圖片驗證機制(Captcha)

我之前推薦過一套 [reCAPTCHA](https://cloud.google.com/security/products/recaptcha) 線上服務非常的好用，不止可以有效阻擋程式機器人對網頁進行灌水，還可以對**英文不好**或**圖像解析能力不夠好的人**進行阻擋，說真的，有時後他們會模糊到有些字我也無法解讀，之前我們就有客戶抱怨說 [reCAPTCHA](https://cloud.google.com/security/products/recaptcha) 提供的驗證圖實在太不清楚了，希望我們提供一個簡易的版本，否則他們網站的使用者可能會不願意使用他們的網站，因此我就寫了一個簡易版的 Captcha 程式。

一般來說 Captcha 機制都是透過 Session 進行驗證，所以 Captcha 圖片都是透過程式送出，而在圖片送出前將顯示在圖片上的文字寫入到 Session\["Captcha"\] 中，等原本的網頁表單送出時進行 Session\["Captcha"\] 與使用者於網頁上輸入的文字進行比對，即可完成 Captcha 的驗證程序。

以下是我之前寫的「超簡易 Captcha 程式」，任何一套入門級的 [OCR](http://en.wikipedia.org/wiki/Optical_character_recognition) 軟體應該都能輕易辨識，所以建議僅用於流量小或人氣不是很旺的小網站中，否則建議對圖像多做出一些變化以避免較強的程式機器人攻擊。

只要新增一個 BuildCaptcha.aspx 頁面 ( 你要自己改成 HttpHandler 或 Generic Handler 也行 )，程式碼如下，我附有非常詳細的註解供參：

```xml
<%@ Page Language="C#" EnableSessionState="True" %>
<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Drawing.Text" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<script runat="server">
    protected void Page_Init(object sender, EventArgs e)
    {
		// (封裝 GDI+ 點陣圖) 新增一個 Bitmap 物件，並指定寬、高
        Bitmap _bmp = new Bitmap(60, 20);
		
		// (封裝 GDI+ 繪圖介面) 所有繪圖作業都需透過 Graphics 物件進行操作
        Graphics _graphics = Graphics.FromImage(_bmp);
        _graphics.Clear(Color.Black);
		// 如果想啟用「反鋸齒」功能，可以將以下這行取消註解
        //_graphics.TextRenderingHint = TextRenderingHint.AntiAlias;

        // 設定要出現在圖片上的文字字型、大小與樣式
        Font _font = new Font("Courier New", 12, FontStyle.Bold);

		// 產生一個 5 個字元的亂碼字串，並直接寫入 Session 裡
		// 請參考: http://www.obviex.com/Samples/Password.aspx )
        Session["Captcha"] = RandomPassword.Generate(5, 5);

        // 將亂碼字串「繪製」到之前產生的 Graphics 「繪圖板」上
        _graphics.DrawString(Convert.ToString(Session["Captcha"]), 
            _font, Brushes.White, 3, 3);

        // 輸出之前 Captcha 圖示
        Response.ContentType = "image/gif";
        _bmp.Save(Response.OutputStream, ImageFormat.Gif);
		
		// 釋放所有在 GDI+ 所佔用的記憶體空間 ( 非常重要!! )
        _font.Dispose();
        _graphics.Dispose();
        _bmp.Dispose();

		// 由於我們要輸出的是「圖片」而非「網頁」，所以必須在此中斷網頁執行
        Response.End();
    }
</script>
```

然後將此 Captcha 圖片連結套用到你的頁面上即可，以下範例包括「重新產生 Captcha 圖片」的程式。

```
<img id="captcha" src="BuildCaptcha.aspx" />
<input type="button" value="變更圖片"
       onclick="captcha.src='BuildCaptcha.aspx?r=' + (new Date()).getTime()" />
```

特別需要一提的是「圖片上的文字」若需要同時顯示「數字」與「英文」者，必須要考量到有些字很容易被**誤判**，例如：I、l、1 這三個字就很不容易區分，還有 O、o、0 在特定字型下也是一樣，另外也建議「特殊符號」不要出現在 Captcha 的文字中。

我在程式碼中有提到 [How To: Generate a Random Password (C#/VB.NET)](https://web.archive.org/web/20040224104645/http://www.obviex.com:80/Samples/Password.aspx) 所提供的範例程式蠻不錯的，但他的密碼產生器有包括特殊符號，建議下載回來後可以修改他的程式將**特殊符號**的部分移除。

**相關連結**

-   [Optical character recognition (OCR) - Wikipedia](http://en.wikipedia.org/wiki/Optical_character_recognition)
-   [CAPTCHA – Wikipedia](http://en.wikipedia.org/wiki/Captcha)
-   [How To: Generate a Random Password (C#/VB.NET)](https://web.archive.org/web/20040224104645/http://www.obviex.com:80/Samples/Password.aspx)
