Revel框架发短信验证码需调用第三方HTTP API(如阿里云、腾讯云),用resty等客户端封装并读取配置;验证码存Redis带TTL,校验后立即删除;登录接口须HTTPS、限流、手机号标准化;校验逻辑必须在Action中实现,不可放Filter。

Revel框架里怎么发短信验证码
Revel本身不内置短信能力,得靠第三方服务(比如阿里云、腾讯云、Twilio)的HTTP API来发。核心是用http.Post或resty这类客户端调用短信服务商接口,而不是找Revel有没有“验证码组件”——它真没有。
常见错误是试图在app.Init里注册全局短信服务,结果忘了每个请求需要独立上下文和超时控制;还有人把API密钥硬编码进conf/app.conf,没做环境隔离。
- 推荐用
resty.New().SetTimeout(5 * time.Second)封装一个复用的HTTP客户端,避免每次新建连接 - 短信模板ID、签名、AppKey必须从
revel.Config.StringDefault("sms.template_id", "")读取,不同环境(dev/staging/prod)走不同配置段 - 别直接在Controller里拼JSON体,先定义结构体再
json.Marshal,方便加字段校验和日志打点
验证码怎么存、存多久、怎么校验
Revel没自带session存储后端,revel.Session默认只支持内存(重启就丢),线上必须配Redis。验证码不能存在客户端cookie或URL里,必须服务端可验证、有时效、防重放。
典型坑是:用session.Set("sms_code", code)存,但没设过期时间,导致一个手机号能无限次用同一码登录;或者用time.Now().Add(2 * time.Minute)算过期时间却忘了序列化进Redis——Redis的EXPIRE得单独调。
- 用
redis.Client.Set(ctx, "sms:"+phone, code, 2*time.Minute),key带前缀+手机号,TTL由Redis托管 - 校验时用
redis.Client.Get(ctx, "sms:"+phone),拿到后立刻Del,防止重复使用 - 别依赖
revel.Session.Id()做绑定,Session ID可能被复用或失效,手机号才是唯一凭据
登录接口怎么设计才安全
POST /api/login/by-sms这种路径没问题,但必须强制HTTPS、CSRF Token(Revel默认开)、手机号格式校验、频率限制。很多人漏掉“同一IP/手机号单位时间请求数”控制,结果被刷号。
错误示例:if len(phone) != 11 —— 这根本拦不住国际号码或带+86前缀的输入;还有人用regexp.MustCompile("^1[3-9]\d{9}$")但没注意Go正则默认不锚定全文,可能匹配到邮箱里的手机号片段。
- 用
libphonenumber库解析并标准化手机号,再提取国家码和本地号,避免只认11位国内号 - 用
golang.org/x/time/rate.Limiter按phone + ip双维度限流,每分钟最多3次请求 - 成功校验后,调
revel.Session.Set("user_id", userId)并设置revel.Session.Options.MaxAge = 3600,别依赖默认值
为什么Revel里不要在Filter里做验证码拦截
想写个SMSAuthFilter统一校验?别这么做。Filter在Action执行前运行,但验证码校验需要访问Redis、可能失败、需要返回400带错误信息——而Filter里只能abort或redirect,没法返回JSON错误体。
更麻烦的是,Filter共享同一个revel.Result上下文,如果在Filter里json.NewEncoder(c.Response.Out).Encode(...),后续Action还会继续执行,造成响应已写入却再写一次的panic。
- 验证码校验逻辑必须放在具体Action里(比如
LoginBySms方法内),才能精确控制状态码和body - 如果多个Action都要用,抽成
func verifySMSCode(c *revel.Controller, phone, code string) (int64, error)工具函数,别塞Filter - Filter只做JWT token校验、权限检查这类无副作用、快速失败的操作
真正难的不是调短信API,而是把手机号、Redis key设计、限流维度、错误响应格式这些细节对齐业务风控要求。比如有些场景要“30秒内不可重复发送”,这个间隔得和服务商回调延迟、网络抖动一起权衡,不能光看文档写的“发送间隔60秒”。


















