结论:不要把验证码生成或校验逻辑塞进 Gin 中间件里,因其执行时机和生命周期不支持返回 ID+base64 图片或绑定 session/IP 的双向交互;应由 handler 负责生成与校验,并严格绑定上下文、使用 Redis 存储、及时删除已验证项。

直接说结论:不要把验证码生成或校验逻辑塞进 Gin 中间件里。中间件的执行时机和上下文生命周期,天然不适合处理需要返回 ID + base64 图片、或需绑定 session/IP 的双向交互流程。
为什么不能用中间件实现验证码生成
常见错误是写一个中间件,在 GET /captcha 路由上挂载,期望它自动生成并写入响应。但问题在于:
- Gin 中间件无法主动终止请求并返回 JSON 或 base64 数据 —— 它只能调用
c.Next()继续往后走,最终还得靠 handler 输出 - 中间件没有路由方法语义(比如区分 GET/POST),容易在非预期路径上触发生成逻辑
- 若在中间件里调用
captcha.Generate(),每次请求都 new 驱动实例,会重复加载字体、初始化绘图上下文,造成内存泄漏和并发 rand.Seed 冲突
base64Captcha.DriverString 与 base64Captcha.DriverDigit 怎么选
二者核心差异在字符集与抗 OCR 能力:
-
DriverDigit只输出纯数字,长度固定(如 5),适合简单场景,但易被脚本爆破 -
DriverString支持自定义Source,推荐设为"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",长度设 4–6,天然提升熵值 - 若项目需中文支持,必须用
DriverChinese并确保字体文件(如wqy-microhei.ttc)存在且路径正确,否则 panic
验证码存储必须脱离默认 DefaultMemStore
单机开发可用 base64Captcha.DefaultMemStore,但上线必须换掉 —— 它不清理过期项,内存持续增长,且多实例部署时无法共享。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- Redis 实现只需满足
base64Captcha.Store接口的Set/Get/Verify三个方法 - key 命名建议加前缀和 TTL,例如
captcha:ip_hash:xxx,避免不同用户碰撞 - 务必在
Verify调用时传true,触发Delete,防止重放;不要等超时自动清理
校验逻辑必须绑定请求上下文,而非仅依赖前端传来的 id
只做 store.Verify(id, input, true) 是严重安全漏洞。攻击者可遍历 id=1 到 id=1000 批量试探。
- 有 session:生成时存
session.Set("captcha_id", id),校验前先session.Get("captcha_id") == id - 无 session(如 API):用
hash.Sum256([]byte(c.ClientIP() + c.GetHeader("User-Agent"))).String()作为 key 前缀,再拼随机 salt - 绝对禁止让前端自由指定
id后直接校验 —— 这等于把验证码池钥匙交出去
最常被忽略的点是:验证码 ID 和答案在生成后必须立即与特定上下文(session / IP+UA)强绑定,且校验成功后必须同步删除。任何环节脱钩,都会让整个机制形同虚设。

















