dchest/captcha 是轻量无依赖的 go 验证码包,默认生成 png,需用 captcha.writeimage 写入 buffer 并设 content-type: image/png 和 no-cache;id 由 captcha.newlen 生成并自动注册,verifystring 校验后 id 立即失效,须严格“一换一”交互;中文需 addfont 注册 ttf;base64 返回需正确编码二进制数据;分布式部署时默认内存 store 不适用,需改用 redis 或 sticky session。

用 dchest/captcha 生成 PNG 验证码图片最直接
Iris 本身不内置图形验证码能力,主流做法是集成 dchest/captcha 这个轻量、无依赖的 Go 包。它默认生成 PNG,支持自定义宽高、字符长度和字体(需手动加载),且不依赖外部服务或 Redis。
关键点在于:调用 captcha.WriteImage() 写入 bytes.Buffer,再通过 http.ServeContent 输出响应,同时必须设置正确的 Content-Type: image/png 和缓存头(Cache-Control: no-cache)——否则浏览器可能复用旧图,导致“明明刚刷新却校验失败”。
- 生成 ID 用
captcha.NewLen(4),不是随机字符串,它会自动注册到内存 store 中 - 不要在 handler 里重复调用
captcha.NewLen,否则旧 ID 对应的验证码就失效了 - 若需中文或特殊字体,得先用
captcha.AddFont()注册 TTF 文件,否则默认用 DejaVuSans,中文会显示为方块
captcha.VerifyString 校验后 ID 自动失效,必须重新生成
这是最容易翻车的地方:captcha.VerifyString("xxx", "user_input") 一旦返回 true,该 ID 就从内部 map 中删除。下一次请求若还拿这个 ID 去校验,必然失败——哪怕用户没输错。
所以前后端交互要严格遵循“一换一”原则:前端每次点击“换一张”,必须向后端请求新 ID(比如 /captcha/new 接口),后端调用 captcha.NewLen() 返回新 ID;用户提交时,把当前 ID 和输入值一起发过来校验。
- 别在登录接口里悄悄重生成 ID,这会让前端持有的图片和后端存储的答案完全对不上
- 如果需要支持“多次尝试”,得自己 wrap 一层逻辑,比如用时间戳拼接 ID 或改用 Redis 存储,但那就脱离
dchest/captcha默认行为了 - 调试时可用
captcha.RandomDigits(4)生成纯数字验证码,方便肉眼比对
Base64 内联图片在 Iris 中要手动拼接 data URL
Iris 的 Context 不像某些框架自动处理 Base64 图片返回,前端要显示验证码,常见做法是后端返回 JSON:{"uuid": "abc123", "img": "base64-encoded-string"},然后前端拼成 data:image/png;base64,xxx。
实现上,先用 captcha.WriteImage(&buf, id, w, h) 写入 buffer,再用 base64.StdEncoding.EncodeToString(buf.Bytes()) 编码。注意:PNG 数据开头有 8 字节 header(\x89PNG\r\n\x1a\n),如果漏掉这部分,解码后图片打不开。
- 别用
string(buf.Bytes())直接转,二进制数据不能当 UTF-8 字符串处理 - 若返回纯二进制 PNG 流(非 Base64),前端要用
fetch().then(r => r.arrayBuffer())拿到原始数据,再URL.createObjectURL(new Blob([ab], {type:'image/png'})) - 移动端 WebView 有时对 data URL 长度敏感,超 20KB 可能渲染异常,此时建议走独立图片 URL 路由
分布式部署时 dchest/captcha 默认不适用
dchest/captcha 的 store 是进程内 map,重启服务或多个 Iris 实例时,验证码 ID 就丢了。若依、Laravel 等框架默认切到 Redis,就是为了解决这个问题。
硬要在集群中用它,只有两个办法:要么所有请求打到同一台机器(加 sticky session),要么自己实现 captcha.Store 接口,对接 Redis 或 etcd。后者工作量不小,因为要处理过期(TTL)、并发读写、序列化等——而 mews/captcha(PHP)或 django-simple-captcha(Python)已经把这些封装好了。
- 本地开发或单机部署,
dchest/captcha完全够用,启动快、零配置 - 上线前务必确认部署模式:K8s 多副本?Nginx 轮询?这些都会暴露内存 store 的局限性
- 临时绕过方案:把验证码答案和 ID 一起写入 JWT payload(仅限低安全要求场景),但有效期必须极短(
VerifyString: false,查不出哪边丢了状态。











