商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > c#如何生成验证码图片_c#生成验证码图片项目实例附完整源码

c#如何生成验证码图片_c#生成验证码图片项目实例附完整源码

  发布于2026-07-17 阅读(0)

扫一扫,手机访问

先说结论:用 System.Drawing 在 .NET 6+ 的跨平台项目里生成验证码图片,这条路基本走不通。要么开启兼容模式,要么直接换成 SkiaSharp。如果是传统的 .NET Framework 4.x 项目,用 Graphics + Bitmap 没什么问题,但要注意字体嵌入和抗锯齿带来的干扰。

这篇文章会把报错原因、跨平台替换方案、以及 ASP.NET Core 中的安全绑定逻辑一次讲清楚。开始之前,先交代一个关键判断:真正麻烦的从来不是画图本身,而是如何在“防机器识别”和“不折磨真实用户”之间找到平衡。

为什么 System.Drawing 在 .NET Core / .NET 5+ 中无法正常工作?

最常见的翻车姿势是 System.PlatformNotSupportedException: System.Drawing.Common is not supported on this platform。从 .NET Core 3.0 开始,微软明确将 System.Drawing 标记为仅 Windows 平台兼容的库,Linux 和 macOS 上的容器或跨平台部署场景默认禁用 GDI+。这个坑几乎每个从 .NET Framework 迁移过来的开发者都踩过。

解决办法不是加个包就完事了——必须显式声明兼容性。在 .csproj 中添加以下配置:


    true

但要注意,这个方案只对 Windows 部署场景有效。如果用 Docker 跑应用,哪怕基础镜像选的是 mcr.microsoft.com/dotnet/aspnet:6.0-windowsservercore,还得确认 GDI+ 组件是否安装到位。相比之下,更稳妥的做法是直接切换到 SkiaSharp——它不依赖系统图形栈,跨平台兼容性好,而且能轻松实现中文、旋转、噪点这些进阶需求。

用 SkiaSharp 生成带干扰线和倾斜字符的验证码图

核心流程不复杂:创建 SKSurface -> 获取 SKCanvas -> 逐字符绘制(带随机角度) -> 加噪点 -> 输出 PNG 流。但在细节处有几个容易翻车的点,值得单独拎出来说:

  • 字符倾斜的处理:别用 RotateDegrees 对整个画布做全局旋转,否则干扰线和背景元素都会跟着歪。正确做法是每个字符单独设置 SKPaint.TextScaleX/YTextAlign,然后通过平移定位。
  • 干扰线的设计:推荐用 SKPath 绘制贝塞尔曲线,比直线更难被 OCR 识别。颜色上别用纯黑(SKColors.Black),改用 new SKColor(80, 80, 80) 降低对比度,既不影响人眼识别,又能增加机器识别难度。
  • 字体的发布:这是一个很常见的坑。直接把 .ttf 文件设置为 CopyToOutputDirectory,加载时用 SKTypeface.FromFile("simhei.ttf"),千万不要依赖系统字体名称(比如 "SimHei" 在 Linux 上不存在)。

下面是一个生成4字符验证码的关键代码片段:

using (var surface = SKSurface.Create(new SKImageInfo(120, 40)))
{
    var canvas = surface.Canvas;
    canvas.Clear(SKColors.White);
    var typeface = SKTypeface.FromFile("simhei.ttf");
    var paint = new SKPaint { Typeface = typeface, TextSize = 24 };
    
    string code = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
    var chars = Enumerable.Range(0, 4).Select(_ => code[new Random().Next(code.Length)]).ToArray();
    
    for (int i = 0; i < chars.Length; i++)
    {
        paint.Color = new SKColor((byte)new Random().Next(50, 150),
                                   (byte)new Random().Next(50, 150),
                                   (byte)new Random().Next(50, 150));
        canvas.DrawText(chars[i].ToString(), 20 + i * 25, 30 + (float)(Math.Sin(i * 0.5) * 5), paint);
    }
    
    var img = surface.Snapshot();
    var data = img.Encode(SKEncodedImageFormat.Png, 100);
    return File(data.ToArray(), "image/png");
}

ASP.NET Core 中如何安全返回验证码并绑定 Session?

验证码值绝对不能放在客户端(比如 hidden field),必须在服务端存储并与当前请求上下文强绑定。不少开发者写进 HttpContext.Session.SetString 后就忽略了过期和并发问题,这是典型的安全漏洞。

以下几点是必须做到的:

  • 设置过期时间HttpContext.Session.SetString("CaptchaCode", code) 的同时,用 HttpContext.Session.SetInt32("CaptchaExpire", DateTime.Now.AddMinutes(5).ToBinary()) 记录时间戳。
  • 验证时检查时间戳if (DateTime.FromBinary(HttpContext.Session.GetInt32("CaptchaExpire") ?? 0) < DateTime.Now) 时直接拒绝。
  • 不要用 GUID 当验证码 IDGuid.NewGuid().ToString() 跟用户 Session 无关,攻击者可以复用同一个验证码图片反复提交。正确的做法是用 HttpContext.Session.Id 进行关联。
  • 分布式缓存的特殊处理:如果用了 Redis 等分布式缓存,Session 数据可能不同步。建议改用 IDistributedCache 存储,key 拼接成 "captcha_" + HttpContext.Session.Id

坦率说,真正考验产品能力的地方不在于画图,而在于平衡。噪点撒过头,移动端截图识别率直接暴跌;字间距窄得亲爹都看不清,视力不佳的用户直接放弃。这些都不是靠几行模板代码能解决的,必须根据具体业务场景的用户反馈反复调优。没有银弹,只有迭代。

本文转载于:https://www.php.cn/faq/2334314.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注