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

C# 驗證碼生成全解析:從 GDI+ 繪圖到 MVC 前後端整合

     編輯:關於C語言

功能需求

  • 生成 4 位英數混合驗證碼(0-9, A-Z, a-z)。
  • 干擾線、噪點、正弦波扭曲,平衡可讀性與機器識別難度。
  • MVC 後端輸出 BMP 圖片,前端支援點擊刷新、AJAX 驗證。

核心類:VerifyCodeHelper

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;

namespace Controllers.Core.Util
{
    public class VerifyCodeHelper
    {
        // 調色盤:高對比度、色盲友好
        private static readonly Color[] Colors = new[]
        {
            Color.FromArgb(220, 20, 60),   // Crimson
            Color.FromArgb(128, 0, 128),   // Purple
            Color.FromArgb(65, 105, 225),  // RoyalBlue
            Color.FromArgb(70, 130, 180),  // SteelBlue
            Color.FromArgb(46, 139, 87),   // SeaGreen
            Color.FromArgb(184, 134, 11),  // DarkGoldenrod
            Color.FromArgb(255, 140, 0),   // DarkOrange
            Color.FromArgb(139, 69, 19),   // SaddleBrown
            Color.FromArgb(0, 191, 255),   // DeepSkyBlue
            Color.FromArgb(95, 158, 160),  // CadetBlue
            Color.FromArgb(255, 20, 147),  // DeepPink
            Color.FromArgb(255, 165, 0)    // Orange
        };

        private static readonly string[] Fonts = { "Arial", "Verdana", "Georgia", "黑體" };
        private const int FontSize = 22;
        private const int Width = 120;
        private const int Height = 40;
        private const int CodeLength = 4;
        private const int NoiseLineCount = 10;
        private const int NoiseDotCount = 100;

        /// 
        /// 生成驗證碼圖片,輸出 code 供 Session 儲存
        /// 
        public static Bitmap CreateVerifyCodeBmp(out string code)
        {
            var bmp = new Bitmap(Width, Height);
            using (var g = Graphics.FromImage(bmp))
            {
                var rnd = new Random();

                // 1. 白底
                g.FillRectangle(Brushes.White, 0, 0, Width, Height);

                // 2. 繪製字元
                var sb = new StringBuilder();
                for (int i = 0; i < CodeLength; i++)
                {
                    string ch = GetRandomChar(rnd);
                    Font font = new Font(Fonts[rnd.Next(Fonts.Length)], FontSize, FontStyle.Bold);
                    Color color = Colors[rnd.Next(Colors.Length)];
                    float x = i * Width / 4f;
                    g.DrawString(ch, font, new SolidBrush(color), x, 0);
                    sb.Append(ch);
                }
                code = sb.ToString();

                // 3. 干擾線
                for (int i = 0; i < NoiseLineCount; i++)
                {
                    using (var pen = new Pen(Colors[rnd.Next(Colors.Length)], 1))
                        g.DrawLine(pen,
                            rnd.Next(Width), rnd.Next(Height),
                            rnd.Next(Width), rnd.Next(Height));
                }

                // 4. 正弦波扭曲
                bmp = TwistImage(bmp, true, 3, rnd.NextDouble() * Math.PI * 2);
            }

            // 5. 噪點(扭曲後重新取 Graphics)
            using (var g = Graphics.FromImage(bmp))
            {
                var rnd = new Random();
                for (int i = 0; i < NoiseDotCount; i++)
                {
                    using (var pen = new Pen(Colors[rnd.Next(Colors.Length)], 1))
                        g.DrawRectangle(pen, rnd.Next(Width), rnd.Next(Height), 1, 1);
                }

                // 6. 邊框
                g.DrawRectangle(new Pen(Color.FromArgb(153, 153, 153)), 0, 0, Width - 1, Height - 1);
            }

            return bmp;
        }

        private static string GetRandomChar(Random rnd)
        {
            int n = rnd.Next(62); // 0-61
            if (n < 10) return ((char)('0' + n)).ToString();
            if (n < 36) return ((char)('A' + n - 10)).ToString();
            return ((char)('a' + n - 36)).ToString();
        }

        /// 
        /// 正弦波扭曲演算法
        /// 
        private static Bitmap TwistImage(Bitmap src, bool xDir, double amplitude, double phase)
        {
            var dest = new Bitmap(src.Width, src.Height);
            using (var g = Graphics.FromImage(dest))
                g.FillRectangle(Brushes.White, 0, 0, dest.Width, dest.Height);

            double baseAxis = xDir ? dest.Height : dest.Width;

            for (int x = 0; x < dest.Width; x++)
            {
                for (int y = 0; y < dest.Height; y++)
                {
                    double dx = xDir ? (Math.PI * 2 * y) / baseAxis : (Math.PI * 2 * x) / baseAxis;
                    dx += phase;
                    double dy = Math.Sin(dx) * amplitude;

                    int srcX = xDir ? x + (int)dy : x;
                    int srcY = xDir ? y : y + (int)dy;

                    if (srcX >= 0 && srcX < src.Width && srcY >= 0 && srcY < src.Height)
                        dest.SetPixel(srcX, srcY, src.GetPixel(x, y));
                }
            }
            return dest;
        }
    }
}

MVC Controller 動作

public ActionResult VerifyCode()
{
    string code;
    Bitmap bmp = VerifyCodeHelper.CreateVerifyCodeBmp(out code);
    // 可選:縮放至 108x36 適應版面
    Bitmap output = new Bitmap(bmp, 108, 36);

    Session["VerifyCode"] = code; // 不區分大小寫驗證時統一 ToLower

    Response.Clear();
    Response.ContentType = "image/bmp";
    output.Save(Response.OutputStream, ImageFormat.Bmp);
    return new EmptyResult();
}

public ActionResult CheckVCode(string vcode)
{
    string sessionCode = Session["VerifyCode"]?.ToString() ?? "";
    bool ok = sessionCode.Equals(vcode, StringComparison.OrdinalIgnoreCase);
    return Json(new { ok }, JsonRequestBehavior.AllowGet);
}

前端整合(jQuery)

<img id="verifyCode" src="" alt="驗證碼" style="vertical-align:middle; cursor:pointer;" />
<a id="refreshVerifyCode" href="javascript:;">看不清?換一張</a>

<script>
$(function () {
    var $img = $('#verifyCode');
    function refresh() {
        $img.attr('src', '/Home/VerifyCode?t=' + Date.now()); // 時間戳防快取
    }
    $img.add('#refreshVerifyCode').on('click', refresh);
    refresh(); // 頁面載入即顯示
});

// 表單送出前驗證
$('#myForm').on('submit', function(e) {
    var vcode = $('#txtVCode').val();
    $.get('/Home/CheckVCode', { vcode }, function(res) {
        if (!res.ok) { alert('驗證碼錯誤'); return false; }
        // 正確則繼續提交
    });
});
</script>

關鍵設計細節

項目說明
字元集排除 0/O、1/I、8/B 等易混淆字元可進一步提升辨識率(本範例為簡化未排除)。
隨機種子每次 new Random() 足夠;高併發下可改用 ThreadLocal<Random> 或 RandomNumberGenerator。
扭曲參數振幅 3、隨機相位,讓 OCR 分割極其困難,人眼仍可辨識。
輸出格式BMP 無壓縮、生成快;若頻寬敏感可改 ImageFormat.Png。
Session 儲存分散式部署需改用 Redis、資料庫或加密 Cookie,避免 Session 丟失。

進階擴充建議

  • 點選/滑塊驗證:引入 GeeTest、SliderCaptcha 等成熟方案,體驗更佳。
  • 快取圖片:預生成 1000 張存 Redis,請求時隨機取用並記錄 code,極大降低 CPU。
  • 無障礙支援:提供語音驗證碼按鈕,符合 WCAG 2.1 AA。

結語

本文完整呈現了從 GDI+ 繪圖核心演算法、MVC 後端輸出、前端交互到部署注意事項的驗證碼全棧實作。代碼結構清晰、參數可配置,可直接納入專案或作為客製化基礎進一步強化安全性。

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