登录防刷离不开验证码。用 GDI+ 绘制验证码图片无需任何控件库,把随机字符、干扰线、噪点画到 Bitmap 上即可输出图片,方法完全独立,窗体或接口场景都能复用。

实现思路

新建指定尺寸的 Bitmap,Graphics 开抗锯齿;先画淡色干扰线,再用随机字体大小与颜色绘制字符,最后铺噪点像素。返回 Bitmap 后由调用方保存或输出。

完整代码

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

public static class CaptchaImage
{
    public static Bitmap Generate(string code, int width = 130, int height = 42)
    {
        var bmp = new Bitmap(width, height);
        var rnd = new Random();
        using (var g = Graphics.FromImage(bmp))
        {
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.Clear(Color.FromArgb(245, 247, 250));

            // 干扰线
            for (int i = 0; i < 5; i++)
            using (var pen = new Pen(Color.FromArgb(rnd.Next(170, 220), rnd.Next(170, 220), rnd.Next(170, 220)), 1.2f))
            {
                g.DrawLine(pen, rnd.Next(width), rnd.Next(height), rnd.Next(width), rnd.Next(height));
            }

            // 字符
            using (var font = new Font("Arial", 24, FontStyle.Bold))
            using (var brush = new SolidBrush(Color.FromArgb(40, 60, 90)))
            {
                float x = 8;
                foreach (char c in code)
                {
                    g.DrawString(c.ToString(), font, brush, x, 6);
                    x += 28;
                }
            }

            // 噪点
            for (int i = 0; i < 90; i++)
                bmp.SetPixel(rnd.Next(width), rnd.Next(height), Color.FromArgb(170, 170, 170));
        }
        return bmp;
    }
}

class Program
{
    static void Main()
    {
        string code = "A8K3";   // 由随机算法生成并存 Session 校验
        using (var bmp = CaptchaImage.Generate(code))
        {
            bmp.Save("captcha.png", ImageFormat.Png);
        }
    }
}

增强方向

  • 每个字符使用随机旋转与颜色,进一步提升 OCR 难度;
  • 字符间加入随机粘连曲线代替直线,视觉效果更接近主流验证码;
  • 服务端保存答案时应同时记录过期时间,防止验证码长期有效。

小结

GDI+ 画验证码逻辑简单、性能好,接口场景可把 Bitmap 转 MemoryStream 后输出为响应流,是 WinForms 与 ASP.NET 通用的写法。