最省事的是用 github.com/mojocn/base64Captcha 库,它封装字体、噪点、干扰线和 base64 编码,返回含 Id 和 Base64String 的结构体;需注意 Id 不可丢、固定宽高、自定义字符集、用 Redis 存储带 TTL 的答案,并警惕校验时空格、大小写及过期问题。

用 github.com/mojocn/base64Captcha 生成 base64 验证码最省事
这个库封装了字体、噪点、干扰线和 base64 编码逻辑,不用自己拼接 PNG buffer 再转 base64。它返回的结构体里直接有 Id 和 Base64String,前端能立刻用,后端也方便存 session 或 Redis 校验。
常见错误是只取了 Base64String 却丢了 Id —— 校验时必须靠 Id 找到原始答案,否则永远失败。
- 初始化时建议固定
width和height(比如120x40),避免前端 img 标签尺寸抖动 - 字符集别用默认的全大写(容易混淆 O/0、I/1),改用
captcha.MustCustomConfig(&captcha.Config{...})显式指定Source: "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" - 别把验证码答案存在内存 map 里跑多实例服务——要用 Redis 或带 TTL 的本地缓存,
Id的过期时间必须和前端倒计时一致(通常 5 分钟)
captcha.VerifyString 校验失败的三个高频原因
这个函数本身不报错,但返回 false 时你往往不知道哪一环断了。最常踩的坑不是逻辑写错,而是数据没对齐。
-
Id字符串前后有空格或换行(尤其从 HTTP header 或 JSON body 解析时),校验前务必strings.TrimSpace(id) - Redis 里存的答案是小写,而用户输入的是大写(或反之),
VerifyString默认区分大小写,要么统一转小写比对,要么初始化 config 时设CaseSensitive: false - 验证码已过期但没删 Redis key,
VerifyString会读到空字符串然后返回false;建议校验前先用GET检查 key 是否存在,不存在就直接拒掉
不依赖第三方库的手动 base64 验证码(仅限调试或极简场景)
真要自己造轮子,核心就三步:生成图像 → 写入内存 buffer → base64 编码。但要注意 Go 标准库的 image/png.Encode 必须传 *bytes.Buffer,不能传 strings.Builder,否则 panic。
立即学习“go语言免费学习笔记(深入)”;
性能上没优势,还容易漏掉抗锯齿、随机字体偏移这些细节,导致识别率低或被 OCR 破解。仅建议用于本地 demo 或学习图像生成流程。
- 用
golang.org/x/image/font/basicfont而不是系统字体,避免部署时缺字体崩溃 - 生成完立刻调
buffer.Bytes(),别反复调buffer.String()(后者会额外分配内存) - base64 编码用
base64.StdEncoding.EncodeToString(buf.Bytes()),别用URLEncoding,前端<img src="data:image/png;base64,...>只认标准编码
并发下验证码 ID 冲突与存储选型建议
base64Captcha 默认用 store.DefaultStore,底层是 sync.Map —— 单机够用,但多实例时完全不共享。如果你用 Nginx 做负载均衡又没粘性 session,用户刷新页面可能拿到 A 实例生成的图,却向 B 实例提交校验,必然失败。
- 生产环境必须换存储:Redis 最稳,用
SET key answer EX 300配合原子操作;如果坚持用本地缓存,得加一致性哈希或用github.com/patrickmn/go-cache并开启 shared mode - ID 生成器别用纯时间戳(
time.Now().UnixNano()),高并发下可能重复;推荐uuid.New().String()或rand.Int63()+ 时间戳拼接 - 验证码答案存 Redis 时,key 名建议带前缀如
captcha:login:{id},避免和其他业务 key 冲突,也方便批量清理
真正麻烦的从来不是生成那几行代码,而是 ID 生命周期管理、存储一致性、大小写和空格这些“看不见”的链路。线上出问题,八成卡在这几步里。










