短信验证码需自行封装HTTP客户端调用第三方API,严格校验手机号、动态加载密钥、设置超时与鉴权头、JSON序列化请求、Redis存储验证码并限流防刷。

短信验证码不是 Fiber 内置能力,得自己封装 HTTP 客户端调第三方 API
Fiber 本身不提供短信发送模块,所有短信逻辑都得你手动对接服务商(如阿里云、腾讯云、容联云、MobTech)。它只负责接收请求、解析参数、返回响应——发短信这一步,本质就是一次带认证头的 POST 请求。别指望 fiber.New() 启动后自动有 SendSMS() 方法。
常见错误现象:c.BodyParser(&req) 成功了,但没校验手机号格式或长度就直接发请求;第三方返回 400 Bad Request 或 401 Unauthorized 却没透出具体错误,前端只看到“发送失败”。
- 必须提前注册服务商账号,拿到
AccessKeyID、AccessKeySecret、SignName、TemplateCode - 手机号要严格校验:用正则
^1[3-9]\d{9}$,别只靠前端传来的字符串 - 请求体必须按服务商文档拼 JSON(比如阿里云要求
PhoneNumbers是字符串,不是数组;腾讯云要求PhoneNumberSet是数组) - 务必设
context.WithTimeout(c.Context(), 10*time.Second),避免第三方接口卡住整个 handler - 不要把密钥硬编码在代码里,用
os.Getenv("SMS_ACCESS_KEY")+godotenv.Load()加载
怎么用 fiber.Ctx 发起带鉴权的短信 POST 请求
别用裸 http.DefaultClient,它不继承 c.Context() 的取消信号。要用 http.Client{Timeout: 10 * time.Second} 配合 c.Context(),确保超时或取消时请求能及时中断。
关键点不是“能不能发”,而是“发错了能不能快速失败并反馈给用户”。比如阿里云返回 {"Code":"isv.BUSINESS_LIMIT_CONTROL"},说明触发频率限制,该返回 429 Too Many Requests,而不是 500 Internal Server Error。
- Header 必须设
"Content-Type": "application/json",漏掉就 415 - Authorization 头格式依服务商而定:阿里云是
"Authorization": "Bearer " + token,腾讯云是"Authorization": "hmac " + signature - 用
json.Marshal()构造请求体,别手拼字符串——中文、特殊符号、转义容易出错 - 收到响应后先检查
resp.StatusCode,再json.Unmarshal(resp.Body, &res),否则 4xx/5xx 响应体里也有有效错误信息 - 记录日志时脱敏手机号:只打
"138****1234",别记完整号
验证码生成、存储、校验三步必须手写,没有现成中间件
Fiber 生态里至今没有类似 Flask-Security 那种带短信验证码流程的封装包。你得自己控制:生成随机 6 位码 → 存 Redis(带 5 分钟 TTL)→ 收到校验请求时查 Redis 并 DEL → 匹配失败直接返回 400。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
高频翻车点:用内存 map 存验证码,一重启就全丢;或者用数据库但没加索引,查 WHERE phone = ? AND expired_at > NOW() 慢成瓶颈。
- 生成建议用
rand.Read()+ base32 编码,比纯数字更抗暴力枚举 - Redis key 建议设计为
sms:verify:{phone},避免不同业务 key 冲突 - 存储时同时存 code 和 timestamp,校验时先比时间再比值,防止重放
- 校验成功后立即
DEL,别等过期——防止同一码多次使用 - 别在 handler 里直接
time.Sleep(1 * time.Second)防刷,这是阻塞式反模式,改用 Redis INCR + EXPIRE 计数
并发发短信要限流,否则被服务商封 IP
第三方短信网关普遍有 QPS 限制(比如阿里云默认 100 QPS),裸起 goroutine 调用 sendSMS() 很容易瞬间打爆。Fiber 不帮你做流量整形,这事得你自己兜底。
最轻量做法:用 golang.org/x/time/rate.Limiter 在全局初始化一个 *rate.Limiter,每次发请求前 if !limiter.Allow() { return c.Status(429).SendString("too many requests") }。
- 别每个 handler 实例都 new 一个 Limiter,必须复用同一个实例
- 限流粒度建议按手机号(
limiterMap[phone]),而不是全局一把锁 - 如果用 Redis 做分布式限流,别用
INCR+EXPIRE两步,要用 Lua 脚本保证原子性 - 注意测试环境绕过限流:用配置项
env == "test"直接跳过limiter.Allow() - 前端按钮点击后禁用 60 秒,只是体验优化,不能替代服务端限流
真正麻烦的从来不是调通 API,而是验证码生命周期管理——生成、存储、过期、删除、防重放、限频,每一步都得自己串起来,且不能依赖 Fiber 的任何扩展点。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










