验证码需用image/draw和image/color手动绘制png图像,背景用image.rgba,字符用位图字体+白名单过滤易混字符,加1–2条浅灰干扰线和10–20个微偏噪点,base64编码后带data:image/png;base64,前缀返回,答案存redis并设ttl,校验后立即删除。

用 image/draw 和 image/color 画验证码字符
Go 标准库不提供现成的验证码生成器,但 image 包足够画出带干扰线、扭曲字符的简单验证码。核心是手动创建 *image.RGBA,用 draw.Draw 填充背景,再用 font.Face + draw.DrawGlyphs 渲染文字——但标准库没内置字体渲染支持,所以更实际的做法是用 golang/freetype(已归档)或轻量替代如 github.com/disintegration/imaging 配合位图字体,或者直接用 ASCII 字符+噪点拼接。
常见错误是直接调 fmt.Sprintf 生成字符串当图片返回,结果 HTTP 响应里全是乱码;验证码必须是图像二进制数据(如 PNG),不是文本。
- 背景用
image.NewRGBA创建,尺寸建议 120×40 起步,太小易被 OCR 破解,太大增加服务端开销 - 字符绘制优先选等宽无衬线位图字体(如
inconsolata.ttf),避免用系统默认字体路径——Linux 容器里往往没有/System/Library/Fonts或C:\Windows\Fonts - 别用
rand.Intn直接生成字符:要过滤掉易混淆字形(如0和O、1和l),推荐白名单:"23456789ABCDEFGHJKMNPQRSTUVWXYZ"
加干扰线和噪点防止 OCR 识别
纯文字+固定字体的验证码,用 tesseract 一跑就过。必须加视觉干扰,但干扰不能过度——否则人眼也识别困难。
干扰线不是越多越好。实测超过 3 条斜线或 5 个随机点,手机端用户失败率明显上升。
立即学习“go语言免费学习笔记(深入)”;
- 用
draw.Line画 1–2 条浅灰(color.RGBA{200,200,200,255})斜线,起点终点都带小幅随机偏移(±10px) - 噪点用
draw.DrawPoint打 10–20 个随机位置的小点,颜色从背景色微调(比如 ±30 的 RGB 偏差),别用纯黑纯白 - 避免在字符主干区域打点——可对每个字符预计算 bounding box,噪点坐标避开这些矩形
生成 base64 图片响应给前端
HTTP 接口不能直接返回 *image.RGBA,必须编码为 PNG/JPEG 再写入 http.ResponseWriter。base64 是最省事的前端集成方式,但要注意 MIME 类型和长度限制。
常见错误是把 PNG 二进制直接转 base64.StdEncoding.EncodeToString 后塞进 JSON,却忘了加 "data:image/png;base64," 前缀,导致 <img src="..." alt="如何在Golang中实现一个基本的验证码生成器 Go语言绘图基础" > 显示空白。
- 用
png.Encode编码到bytes.Buffer,再转 base64 字符串 - 响应 JSON 字段名别叫
captcha,建议用image或src,和前端<img alt="如何在Golang中实现一个基本的验证码生成器 Go语言绘图基础" >的src属性语义一致 - 别在服务端长期缓存生成的图片对象——
*image.RGBA占内存,且验证码本就是一次性的
验证码文本怎么安全传给后端校验
图形本身不包含可提取的文本,必须在生成时把答案存起来,等用户提交时比对。关键不是“怎么画”,而是“怎么记、怎么查、怎么删”。
最容易被忽略的是存储时效和清理机制。Redis 设置 EXPIRE 是必须的,但很多实现只存 key-value,没做请求频率限制,结果被暴力轮询。
- 用随机字符串作 session ID(如
uuid.NewString())当 key,值存验证码文本 + 过期时间戳 - 生成接口返回该 ID 给前端,用户提交校验时带着 ID 和输入文本一起 POST
- 校验成功或失败后,立即
DEL对应 key——不要等自动过期,防止重放攻击 - 如果不用 Redis,至少用带 TTL 的内存 map(如
github.com/patrickmn/go-cache),别用全局变量存 map,goroutine 不安全










