首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 使用 GDI+ 繪製驗證碼完整教程:從隨機碼生成到 Session 驗證

     編輯:關於C#

驗證碼演進:從圖片拼接到 GDI+ 動態繪製

早期驗證碼多採用預製數字/字母圖片拼接,靈活性差、素材維護成本高。現代方案採用 GDI+ 即時繪製單張圖片,可任意控制字元集、干擾線、噪點、扭曲變形,安全性與靈活性雙贏。

核心步驟

  1. 生成隨機字元串(支援純數字、大小寫混合、大寫+數字等模式);
  2. 建立 Bitmap 畫布,創建 Graphics 繪圖面;
  3. 設定字體、筆刷、背景、干擾元素;
  4. 調用 Graphics.DrawString 繪製字元;
  5. 輸出 JPEG 流至 Response.OutputStream;
  6. 將驗證碼存入 Session,後續表單提交時比對驗證。

完整實現代碼

1. 隨機碼生成器


public enum RandomGeneratorStyle {
    Number,                 // 純數字
    NumberAndChar,          // 數字 + 大小寫字母
    NumberAndCharIgnoreCase // 數字 + 大寫字母
}

public static class RandomGenerator {
    private static readonly Random _rnd = new Random();

    public static string Generate(RandomGeneratorStyle style, int length) {
        string source = style switch {
            RandomGeneratorStyle.Number => "0123456789",
            RandomGeneratorStyle.NumberAndChar => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
            RandomGeneratorStyle.NumberAndCharIgnoreCase => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
            _ => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        };

        char[] chars = new char[length];
        for (int i = 0; i < length; i++) {
            chars[i] = source[_rnd.Next(source.Length)];
        }
        return new string(chars);
    }
}

2. 繪製驗證碼圖片 (Generic Handler .ashx 推薦)


public class CaptchaHandler : IHttpHandler {
    public bool IsReusable => false;

    public void ProcessRequest(HttpContext context) {
        // 參數:長度、樣式、寬高可從 QueryString 讀取,這裡用預設值
        int length = 4;
        RandomGeneratorStyle style = RandomGeneratorStyle.NumberAndCharIgnoreCase;
        int width = (int)Math.Ceiling(length * 18.5); // 字體 14px 寬度約 18.5
        int height = 30;

        string code = RandomGenerator.Generate(style, length);
        context.Session["CaptchaCode"] = code; // 存 Session 驗證

        using (Bitmap bmp = new Bitmap(width, height))
        using (Graphics g = Graphics.FromImage(bmp)) {
            // 背景
            g.Clear(Color.White);
            // 干擾線
            DrawNoiseLines(g, width, height, 3);
            // 干擾點
            DrawNoisePoints(g, width, height, 50);
            // 字體
            using (Font font = new Font("Consolas", 16, FontStyle.Bold | FontStyle.Italic))
            using (SolidBrush brush = new SolidBrush(Color.FromArgb(_rnd.Next(0, 100), _rnd.Next(0, 100), _rnd.Next(0, 100)))) {
                // 逐字元繪製,隨機偏移、旋轉增加識別難度
                for (int i = 0; i < code.Length; i++) {
                    float x = i * 18 + _rnd.Next(2);
                    float y = _rnd.Next(-2, 4);
                    g.TranslateTransform(x, y);
                    g.RotateTransform(_rnd.Next(-15, 15));
                    g.DrawString(code[i].ToString(), font, brush, 0, 0);
                    g.ResetTransform();
                }
            }
            // 輸出
            context.Response.ContentType = "image/jpeg";
            bmp.Save(context.Response.OutputStream, ImageFormat.Jpeg);
        }
    }

    private void DrawNoiseLines(Graphics g, int w, int h, int count) {
        using (Pen pen = new Pen(Color.LightGray, 1)) {
            for (int i = 0; i < count; i++) {
                g.DrawLine(pen, _rnd.Next(w), _rnd.Next(h), _rnd.Next(w), _rnd.Next(h));
            }
        }
    }

    private void DrawNoisePoints(Graphics g, int w, int h, int count) {
        using (SolidBrush brush = new SolidBrush(Color.Gray)) {
            for (int i = 0; i < count; i++) {
                g.FillRectangle(brush, _rnd.Next(w), _rnd.Next(h), 1, 1);
            }
        }
    }
}

3. 前端調用

<img src="CaptchaHandler.ashx" id="captchaImg" alt="驗證碼" style="cursor:pointer;vertical-align:middle;" />
<a href="javascript:void(0);" onclick="document.getElementById('captchaImg').src='CaptchaHandler.ashx?' + Math.random();">看不清?換一張</a>
<br />
<input type="text" id="txtCaptcha" name="captcha" maxlength="4" />

4. 後端驗證


public static bool Validate(string userInput, HttpContext context) {
    if (context.Session["CaptchaCode"] is string serverCode) {
        // 不區分大小寫比對
        return string.Equals(userInput, serverCode, StringComparison.OrdinalIgnoreCase);
    }
    return false;
}

// 在登入/提交 Action 中調用
if (!CaptchaHelper.Validate(Request.Form["captcha"], HttpContext.Current)) {
    ModelState.AddModelError("captcha", "驗證碼錯誤");
    return View();
}

進階強化方向

增強項實現思路
字元扭曲變形使用 GraphicsPath.AddString + GraphicsPath.Warp 對字元路徑做隨機透視變形
背景漸變/圖片LinearGradientBrush 或 TextureBrush 填充背景
中文驗證碼字元源改為常用漢字庫,字體選「微軟雅黑」,寬度適當加大
點擊/滑塊/行為驗證前端集成 GeeTest、滑動拼圖、點選文字等無感驗證 SDK,後端二次校驗
分散式 SessionSession 改用 Redis、資料庫,支援 Web Farm 負載均衡

常見坑與最佳實踐

  • Session 丟失:確保 SessionState 模式正確(InProc/StateServer/SQLServer/Redis),驗證碼頁面與表單提交在同一應用域。
  • 圖片不顯示:檢查 Response.ContentType、無多餘輸出、HttpHandler 註冊正確(web.config <handlers> 或 .ashx 自動映射)。
  • 隨機數重複:Random 實例化頻率過高會產生相同種子,改用 static readonly Random 或 RandomNumberGenerator (CSP)。
  • 無障礙訪問:提供語音驗證碼按鈕,alt 屬性提示「驗證碼圖片,點擊更換」。

總結

GDI+ 繪製驗證碼是 ASP.NET 經典技能,核心在於 Bitmap + Graphics + DrawString 三板斧。本文提供從隨機生成、干擾繪製、Session 存儲到驗證比對的完整閉環代碼,並給出進階強化方向。實際專案中可根據安全等級選擇「簡單圖形驗證碼」或「第三方行為驗證」,兼顧用戶體驗與防刷效果。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved