Buffalo中需用captcha或gocaptcha库配合buffalo.Context生成验证码图片,关键点包括:手动设置Content-Type、将id存session、调用captcha.Generate获取id与answer、核验时用captcha.VerifyString并及时清理、防缓存需加响应头或随机参数、高并发须替换为带TTL的store。

验证码图片生成要用 buffalo.Context 配合 gocaptcha 或 captcha 库
Buffalo 本身不内置图形验证码,得靠第三方库生成 base64 图片或写入 response。主流选 captcha(by r9y9)或轻量级 gocaptcha,前者更稳定、支持音频(虽少用),后者 API 更直白。
关键点是:不能在 handler 里直接调 captcha.NewDigit 后立刻写图片——Buffalo 的 c.Context.Response() 是封装过的 http.ResponseWriter,需手动设置 Content-Type 并调 Write;否则浏览器收不到图片流。
- 生成时务必用
captcha.Generate得到id和answer,id要存进 session(如c.Session().Set("captcha_id", id)),answer不能明文存 cookie - 图片输出前必须调
c.Response().Header().Set("Content-Type", "image/png") - 别用
fmt.Println打印验证码答案——本地调试可以,上线必须删,否则等于把答案打在响应里
核验逻辑要读 session 中的 captcha_id 再查 captcha.VerifyString
用户提交表单后,先从 c.Param("id") 或表单字段取前端传来的 captcha_id,再从 session 取服务端存的原始 id 做比对——这步不能省,否则攻击者可复用任意已知 id 猜答案。
captcha.VerifyString 是阻塞式校验,内部会查内存 store(默认是 defaultStore),所以必须确保生成和核验用的是同一个 store 实例(全局单例即可)。
Buffalo框架 1.0.1 版本源码包下载,适合需要错误处理改进、依赖更新、render.Download 注释和 request logger 调整的 v1 项目。
- 核验失败要清掉该
id对应的 store 记录:captcha.Remove(id),防重放 - 别直接比对表单值和 session 里的 answer——
captcha.VerifyString已做大小写忽略、空白过滤,自己比容易绕过 - 如果用了自定义 store(如 Redis),确认
Get返回的不是 nil,否则VerifyString会 panic
前端请求 /captcha.png?id=xxx 时要处理 404 和缓存问题
浏览器会缓存图片 URL,导致验证码不刷新。常见做法是在 img src 后加时间戳或随机数,比如 /captcha.png?id=;但更稳妥的是服务端加响应头。id&t=Date.now()
- 在生成图片的 handler 结尾加上:
c.Response().Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0") - 前端 JS 换图时,用
img.src = "/captcha.png?id=" + id + "&r=" + Math.random(),避免代理或中间件缓存 - 如果返回 404,检查
c.Response().Write()是否被前面的c.Render或c.Error提前触发——Buffalo 的响应只能写一次
并发下 captcha 默认内存 store 有泄漏风险
默认的 defaultStore 是基于 map + mutex 的内存实现,不自动清理过期项。高并发场景下,若用户没完成校验就关页,id 会一直占内存。
- 上线前务必替换为带 TTL 的 store,例如用
github.com/mikespook/captcha/store/redis(需适配 Buffalo 的 Redis client) - 或者用
github.com/kataras/golog配合定时任务定期captcha.Clean,但不如外部 store 可靠 - 测试时用
go tool pprof http://localhost:3000/debug/pprof/heap看 goroutine 和 map size,确认无持续增长
实际集成时最易漏的是 store 生命周期管理与响应头控制——这两处出问题,要么验证码总显示空白,要么一刷新就失效,且日志里几乎不报错。










