能做但不推荐在新项目中使用 System.Drawing 生成验证码,因其在 Linux 容器或。NET 6+ 无 UI 环境下会抛 PlatformNotSupportedException;推荐改用 ImageSharp 实现跨平台、可控内存的验证码图像生成。

怎么用 System.Drawing 生成带噪点的验证码图片
直接说结论:能做,但不推荐在新项目里用 —— System.Drawing 在 Linux 容器或 .NET 6+ 的无 UI 环境下默认不可用,运行时会抛 System.PlatformNotSupportedException。真要快速验证逻辑,本地 Windows + .NET Framework 还行;上云或跨平台,得换方案。
如果非要用,核心是三步:生成随机字符串 → 绘制文字 → 叠加噪点。关键不是“画得多好看”,而是让 OCR 和简单图像处理难识别,同时人还能认出来。
-
Graphics.DrawString()画文字时,别用固定字体大小和位置,加点轻微旋转(±5°)和 X/Y 偏移(±2px) - 噪点别用纯黑 / 白点,用接近背景色的灰度值(比如背景是
Color.White,噪点就用Color.FromArgb(240, 240, 240)) - 每张图至少打 100~200 个噪点,太密变马赛克,太少形同虚设;建议用
Random.Next()控制坐标,但注意别堆在文字上——先画文字,再画噪点
ImageSharp 替代 System.Drawing 的实操要点
ImageSharp 是目前最稳妥的跨平台图像处理选择,支持 .NET 5+、Linux、Docker,且内存占用比 GDI+ 更可控。但它没有现成的“验证码组件”,得自己组合像素操作。
- 用
Image<rgba32>.Create()</rgba32>创建画布,宽高建议 120×40 起,太小容错率低,太大增加传输开销 - 文字绘制靠
TextOptions:必须显式设置Font(推荐用内置的Fonts.FiraCode或自带的Fonts.Arial),否则默认字体可能缺失导致空白 - 噪点用
image[x, y] = new Rgba32(……)随机写像素,别用DrawPoint()——它底层仍走路径渲染,慢且不必要 - 最后调用
image.SaveAsPng(stream),别用Save(),否则可能输出 PNG 但 MIME 类型不对,前端显示为空
验证码字符串怎么生成才不容易被爆破
重点不在“随机”,而在“防预计算”和“防重放”。单纯 new Random().Next() 不够,尤其在高并发下容易重复或可预测。
- 字符集避开
0/O/1/l/I这类易混淆字母数字,推荐用"23456789ABCDEFGHJKLMNPQRSTUVWXYZ"(26 位) - 长度固定 4~6 位,别用 8 位——用户输错率陡增,反而降低安全性
- 服务端生成后,必须存进分布式缓存(如 Redis),Key 带过期时间(
SET key value EX 180),别存在 Session 或内存里 - 返回给前端的不只是图片,还得有唯一
captchaId,校验时比对这个 ID 对应的缓存值,而不是靠图片 URL 参数传值
为什么验证码接口常被绕过?几个硬伤点
不是图不够花,而是整个链路有断点。常见失效场景根本不在图像本身。
- 前端没禁用输入框自动填充(
autocomplete="off"不管用),浏览器填了旧验证码还提交成功 - 接口没校验请求来源 IP 或 User-Agent 频次,单 IP 1 秒发 10 次获取请求,就等于批量拿图
- 校验成功后没清空 Redis 中的
captchaId,导致同一串码能反复用 - 图片响应头漏了
Cache-Control: no-store, no-cache,CDN 或浏览器缓存了验证码图,刷新也不变
图像只是第一道门,后面每个环节松动一点,整套就形同虚设。特别是缓存清理和请求频控,最容易被忽略,也最难事后排查。