4.4 KiB
4.4 KiB
验证码(SMS 模块)
邮箱与手机 OTP 统一走 POST /v1/sms/codes。校验在登录 / 注销等业务里调用 sms logic,不单独暴露校验 HTTP。
OpenAPI:GET /openapi.yaml → /v1/sms/codes。错误码目录:/docs/errors(限流 42902)。
发码接口
| 项 | 规则 |
|---|---|
| 路径 | POST /v1/sms/codes |
| 登录 | 无需 Bearer |
注销(purpose=2) |
需要 Bearer;target 必须是当前账号绑定邮箱(不支持向手机号发注销码) |
| Header | X-Language 必填(zh-CN / en / zh-TW),用于邮件主题/正文;非法或缺失 → 40001 |
| Body | purpose:1=登录,2=注销;target:邮箱或大陆 11 位手机号(可带 +86),服务端识别,不要再传 channel |
成功 data |
{"expires_in":300} — 验证码有效秒数(固定 300,与 Redis TTL 一致) |
网关须写入 X-Real-IP(或 PROXY 解析后的等价头)。限流键用 reqctx.RealIP,不用 gnet RemoteAddr(C6)。
封控规则(两层,都会返回 42902)
客户端应:同一 target 至少间隔 60s 再点「发送」;并避免同一公网 IP 在短时间内狂刷登录/发码/刷新。
1. HTTP 层:按客户端 IP 令牌桶
| 项 | 值 |
|---|---|
| 中间件 | AuthRateLimit(在 Auth 之前) |
| 限流键 | RealIP(空则记为 unknown) |
| 算法 | 每 IP 一个令牌桶:rate = burst = auth_per_second |
| 配置 | rate_limit.auth_per_second(configs/config.yaml,默认 5) |
| 命中路径 | POST /v1/sms/codes、POST /v1/auth/login、POST /v1/auth/token/refresh(三条共用同一 IP 桶) |
| 响应 | HTTP 200,code 42902,msg 随语言(如「请求过于频繁」/ rate limited) |
说明:这是进程内限流,多实例各自计数;前面 Nginx 仍可再加 WAF / 全局限流。
2. 业务层:按「渠道 + 用途 + 目标」发送冷却
| 项 | 值 |
|---|---|
| 冷却时长 | 60 秒(代码常量 defaultCodeCooldownSeconds,当前不可 YAML 配置) |
| 粒度 | 规范化后的 target + purpose + 渠道(邮箱 / 手机各算一条) |
| 存储键(逻辑) | {channel}:{purpose}:{target},例如 1:1:ada@example.com |
| Redis | 冷却:SETNX sms:cd:{逻辑键} TTL 60s;验证码:sms:code:{逻辑键} TTL 300s |
| 行为 | 60s 内对同一 target+purpose+渠道再次发码 → 42902(不覆盖未过期的码) |
| 发送失败 | SMTP / 阿里云返回错误时会 Del 该逻辑键(含冷却),可立即重试(仍受 IP 桶约束) |
独立计数示例(互不影响冷却):
purpose=1与purpose=2各 60s 一条;- 同一邮箱与同一手机号是不同
channel; - 不同邮箱 / 不同手机号各自 60s。
客户端建议
| 场景 | 建议 |
|---|---|
| UI 倒计时 | 以 60s 为准(与业务冷却一致);expires_in=300 只表示码多久失效 |
收到 42902 |
不要立刻连点;至少等待 60s 或提示用户稍后再试 |
| 多 Tab / 多设备 | 同一 target 共享冷却;不同 IP 仍共享 target 冷却 |
验证码本身
| 项 | 规则 |
|---|---|
| 长度 | 6 位数字 |
| 有效期 | 300s(5 分钟) |
| 校验 | 登录 / 注销等业务调用 CheckAndConsume:正确则删除(一次性);错误 / 过期 / 已用过 → 40103(统一「验证码无效」) |
| 校验限流 | 无单独「输错 N 次锁定」;仍受登录等接口的 IP 桶约束 |
测试与白名单(不走真实 SMTP/短信)
| 类型 | 规则 |
|---|---|
| 邮箱 | test01@spark.com~test10@spark.com:Send 不发邮件、不写 Redis 冷却;登录 / 注销校验 任意 verify_code |
| 手机 | +8613800138000~+8613800138002:不发短信;校验任意码(与测试邮箱类似) |
配置速查
| 配置项 | 作用 |
|---|---|
rate_limit.auth_per_second |
IP 桶速率(默认 5/s,与 login / refresh / 发码共用) |
sms.smtp.* |
邮箱 OTP(DirectMail SMTP) |
sms.aliyun_sms.* |
手机 OTP(按运营商模板) |
| Redis | 生产存 OTP + 冷却;无 Redis 时进程内 MemoryStore(单实例开发) |
OTP 的 60s 冷却 与 300s 有效期 在 internal/module/sms/logic/service.go 常量中;若要运营可调,需改代码并重新发布。