使用github.com/mojocn/base64captcha而非手写image库,因其封装字体渲染、噪声、旋转等抗ocr策略,直接输出base64字符串,避免i/o与http配置;需复用driver、替换默认内存存储、绑定会话/ip校验、设有效期并及时删除、禁用debug及定制字体。

验证码图片生成要用 github.com/mojocn/base64Captcha 而不是自己手撸
自己用 image 标准库画干扰线、扭曲字体、加噪点,看似可控,实际容易被 OCR 破解,且抗压能力差(并发生成时 rand.Seed 冲突、内存泄漏常见)。base64Captcha 封装了字体渲染、噪声、旋转、倾斜等策略,输出直接是 base64 字符串,省去文件 I/O 和 HTTP 静态服务配置。
关键实操点:
- 初始化时必须调用
captcha.SetCustomStore换掉默认的内存存储,否则重启服务后所有验证码失效,且无法跨实例共享 - 不要在 handler 里每次 new 一个
captcha.DriverDigit,复用驱动实例,避免重复加载字体文件和初始化绘图上下文 - 字符长度建议设为 4–6,过长易输错,过短易爆破;数字+大小写字母组合比纯数字更抗简单脚本
校验逻辑必须绑定用户会话或请求指纹,不能只靠前端传来的 ID
常见错误是让前端拿到 captchaId 后直接 POST 到校验接口,后端仅查 captcha.VerifyString(id, input)。这会导致攻击者遍历 captchaId(比如从 1 到 1000)批量重放请求。
安全做法是把验证码绑定到具体上下文:
- 登录场景:生成验证码时,将
captchaId存入当前 session(如session.Set("captcha_id", id)),校验前先比对 session 中的 ID 是否匹配 - 无 session 场景(如 API):用请求 IP + User-Agent 的哈希值作为存储 key 前缀,再拼上随机 salt,避免 IP 池穿透
- 务必设置验证码有效期(
config.Expire默认 10 分钟),且校验成功后立即调用captcha.Delete(id),防止重放
gin-gonic/gin 中集成要避开中间件生命周期陷阱
有人把验证码生成写成 gin 中间件,结果发现每次请求都触发生成,或者校验时中间件已执行完毕、上下文丢失。根本原因是中间件顺序和 context 生命周期不匹配。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
正确姿势是拆成两个独立路由 handler:
-
GET /captcha:生成并返回{"id": "xxx", "png": "data:image/png;base64,..."},不走鉴权中间件 -
POST /login:在业务 handler 开头手动校验,例如:if !captcha.VerifyString(captchaID, userCode) { c.JSON(400, gin.H{"error": "captcha mismatch"}) return } - 切勿在
POST路由上套「校验 captcha」中间件——它无法知道哪个字段存的是用户输入,也无法访问表单解析后的值
生产环境必须替换默认字体和禁用调试模式
base64Captcha 默认用 utils/fonts/Roboto-Regular.ttf,但这个字体在 Alpine Linux(Docker 默认镜像)里常因缺少 freetype 库而 panic,错误信息是 font: failed to parse font: invalid ttf header。
解决方式:
- 用
go:embed打包一个精简版 TTF(如DejaVuSans.ttf),通过driver.WithFontBytes注入 - 关闭
config.Debug = false,否则会把明文验证码写进响应头,形同裸奔 - 如果用 Redis 存储,注意
store.RedisStore的KeyPrefix要带业务标识(如"auth:captcha:"),避免和其他模块 key 冲突
最麻烦的其实是测试环节:本地跑通不等于线上可用,特别是字体路径、并发存储竞争、Redis 连接池耗尽这些点,往往只在压测时暴露。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










