topfans/docs/superpowers/specs/2026-05-22-sms-register-design.md
zheng020 c05953b84b docs: 修正技术选型说明
明确短信服务使用直接AccessKey认证,与OSS的STS方式区分。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:43:44 +08:00

12 KiB
Raw Blame History

注册短信验证码功能设计文档

  • 创建日期: 2026-05-22
  • 状态: 待评审
  • 目标: 在用户注册流程中加入阿里云短信验证码验证

1. 背景与需求

当前注册流程:手机号 + 密码 → 设置昵称 → 完成注册

需求:在注册前增加短信验证码验证,确保手机号真实有效,防止恶意注册。


2. 交互流程设计

2.1 前端页面变化

修改 frontend/pages/register/register.vue

  1. 用户输入手机号后,点击"发送验证码"按钮
  2. 按钮变为倒计时状态60 秒),期间不可点击
  3. 用户输入收到的验证码,点击"验证验证码"按钮
  4. 验证通过后:解锁密码输入框,按钮变为"注册"
  5. 用户输入密码,点击"注册"按钮
  6. 页面跳转到 /pages/profile/setNickname,携带手机号+密码+verify_token

注册流程图:

register.vue                    setNickname.vue
    │                                  │
    ├── 输入手机号                      │
    ├── 点击"发送验证码" ──→ 发送验证码   │
    ├── 输入验证码                      │
    ├── 点击"验证验证码" ──→ 验证通过    │
    │        返回 verify_token          │
    ├── 输入密码                        │
    ├── 点击"注册" → 存储 temp_*        │
    │        + verify_token            │
    └── 跳转 setNickname ─────────────→ │
                                     ├── 读取 temp_* + verify_token
                                     ├── 输入昵称 + 选择 star_id
                                     └── 点击"下一步" ──→ register API
                                              (mobile, password, nickname, star_id, verify_token)

2.2 验证码状态

状态 描述
未发送 默认状态,输入框禁用
已发送(倒计时中) 输入框启用,倒计时按钮显示剩余秒数
已发送(可重发) 倒计时结束,按钮恢复可点击
验证成功 输入框显示绿色对勾,继续注册流程
验证失败 显示错误提示,用户可重新发送

2.3 后端流程

发送验证码流程:
用户输入手机号 → 发送验证码请求 → 后端生成6位随机码 → 存入RedisHash结构60秒TTL → 调用阿里云SMS API → 返回前端

验证验证码流程:
用户输入验证码 → 验证请求 → 检查Redis中验证码是否存在 → 比对code字段 → 失败则递增attempts≥3次则删除Key → 验证成功则删除验证码Key并创建verify:register:{mobile}记录300秒TTL → 返回verify_token

注册流程需携带verify_token
前端提交注册信息 → 后端检查verify:register:{mobile}是否存在且匹配 → 不匹配则拒绝 → 匹配则处理注册(创建用户、粉丝档案) → 删除verify:register记录 → 返回注册成功

3. 实现方案

方案一:在 userService 中直接集成 SMS SDK推荐

前端 → 网关 /auth/send-code → userService → 阿里云SMS
前端 → 网关 /auth/verify-code → userService → Redis验证 → 继续注册流程

优点:

  • 注册逻辑集中在一个服务,流程一目了然
  • userService 已有完整注册流程,加 SMS 只是顺带的事
  • Redis 已在 userService 中可用,不用跨服务调用
  • 部署简单,只增加一个 SDK 依赖

缺点:

  • userService 多了一个外部依赖(阿里云 SMS SDK升级 SDK 时需重新编译
  • 如果未来短信需求变复杂userService 会越来越臃肿

适用场景:当前阶段最合适,注册流程本来就在 userService不需要为了"未来的扩展性"增加复杂度。


方案二SMS 单独作为一个微服务

前端 → 网关 /auth/send-code → 短信Service → 阿里云SMS
前端 → 网关 /auth/verify-code → userService → Redis验证

优点:

  • 职责单一,短信服务可以独立迭代
  • 未来如果加邮件、推送通知,继续扩展这个服务即可
  • userService 保持干净,不感知第三方服务细节

缺点:

  • 需要额外部署一个服务,增加运维复杂度
  • 注册流程跨两个服务,一次注册调用两次 Dubbo延迟增加
  • 目前项目没有独立的通知服务,新增一个微服务成本偏高

适用场景:适合未来有多渠道通知需求的大规模系统。


方案三:在 gateway 层调用 SMS

前端 → 网关 /auth/send-code → 阿里云SMS网关直接调用
前端 → 网关 /auth/verify-code → userService → Redis验证

优点:

  • 阿里云 credentials 配置原本就在 gatewayOSS 用的是同一套),直接复用
  • 前端请求在网关层就能触发短信,不用穿透到后端服务
  • 网关是单点,处理短信很方便

缺点:

  • 短信发送成功后的后续注册流程还在 userService逻辑割裂
  • 网关承担了越来越多的职责路由、认证、OSS上传、现在又加短信越来越重
  • 如果 SMS 认证配置要从 gateway 复用,需要确保 userService 能访问到同一份配置,增加了部署耦合

适用场景:适合短信作为独立操作、不属于用户注册主流程的场景。


4. 方案对比

方案一userService 方案二(独立微服务) 方案三gateway
部署复杂度
延迟
逻辑内聚性 (注册+短信在一起) 高(短信独立) 差(分散在两处)
可扩展性
维护成本

5. 技术选型

项目 选择
短信 SDK github.com/aliyundysms/dysmsapi-go-sdk(阿里官方 Go SDK需确认最新包名
认证方式 AccessKeyID/Secret短信服务使用直接 AccessKey非 OSS 的 STS 方式)
验证码存储 Redis已部署过期自动失效
推荐方案 方案一userService

6. 配置项

backend/services/userService/config/ 下新增 SMS 配置(采用方案一,配置跟随服务):

// SMSConfig 短信配置
type SMSConfig struct {
    AccessKeyID     string
    AccessKeySecret string
    SignName        string        // 短信签名
    TemplateCode    string        // 短信模板CODE
    Region          string        // 区域(默认 cn-hangzhou
}

通过环境变量注入(与 gateway 的 OSS 配置分开,避免部署耦合):

# userService 环境变量
SMS_ACCESS_KEY_ID=
SMS_ACCESS_KEY_SECRET=
SMS_SIGN_NAME=TopFans
SMS_TEMPLATE_CODE=SMS_xxxxxxx
SMS_REGION=cn-hangzhou

7. Redis 存储设计

7.1 Key 结构(按场景区分)

场景 Key 格式 说明
注册 sms:register:{mobile} 注册时发送的验证码
登录 sms:login:{mobile} 登录时发送的验证码(未来扩展)
修改密码 sms:password:{mobile} 忘记密码时发送的验证码(未来扩展)

7.2 Value 结构Hash 类型,支持更多元数据)

Key: sms:register:13800138000
Value (Hash):
  code: "123456"           // 6位验证码
  created_at: "1700000000"  // 发送时间Unix timestamp
  attempts: "0"             // 验证失败次数超过3次需重新获取
  used: "false"             // 是否已使用
TTL: 60 秒

7.3 防暴力破解策略

  • 失败计数:每次验证失败递增 attempts 字段,≥ 3 次后删除 Key要求用户重新获取
  • 发送频率限制:每小时最多发送 10 次,超限返回 429
    • 使用独立 Key sms:limit:register:{mobile} 计数String 类型TTL=3600 秒)
  • IP 维度限流(可选):每 IP 每小时最多请求 30 次
    • 使用独立 Key sms:limit:ip:{ip} 计数String 类型TTL=3600 秒)

7.4 验证后处理

验证成功后:

  1. 删除验证码 Key防止重放DEL sms:register:{mobile}
  2. 创建验证通过记录:verify:register:{mobile} = {token}TTL = 300 秒
  3. 注册接口使用 verify_token 比对,一致则处理注册,注册成功后删除该记录

8. API 设计

8.1 发送验证码

请求

POST /api/v1/auth/send-code
Content-Type: application/json

{
    "mobile": "13800138000"
}

响应

{
    "code": 200,
    "message": "发送成功",
    "data": {
        "expires_in": 60
    }
}

8.2 验证验证码

请求

POST /api/v1/auth/verify-code
Content-Type: application/json

{
    "mobile": "13800138000",
    "code": "123456"
}

响应

{
    "code": 200,
    "message": "验证成功",
    "data": {
        "verified": true,
        "verify_token": "vtf_abc123xyz789...",
        "expires_in": 300
    }
}

说明

  • verify_token 是长度为 32 位的随机字符串(格式:vtf_ + 29 位随机字符),不是 JWT
  • 存储在 Redis 中:verify:register:{mobile}vtf_abc123xyz789...TTL = 300 秒
  • 注册接口需携带此 token验证通过后才处理注册请求
  • 验证成功后删除该记录,防止重复使用

注册接口携带 token

POST /api/v1/auth/register
Content-Type: application/json

{
    "mobile": "13800138000",
    "verify_token": "vtf_abc123xyz789...",
    "password": "xxx",
    "nickname": "xxx",
    "star_id": 1
}

后端逻辑:

  1. 根据 mobile 从 Redis 中获取 verify:register:{mobile} 的值
  2. 与请求中的 verify_token 比对,不一致则拒绝
  3. 验证通过后删除该记录,防止重复使用

9. 错误处理

场景 错误码 处理方式
手机号格式错误 400 提示"手机号格式不正确"
短信发送失败(阿里云超时) 500 提示"服务暂不可用,请稍后重试"
短信发送失败(配额不足) 500 提示"发送失败,请联系客服"
Redis 连接失败 500 提示"验证服务暂不可用"
验证码已过期 400 提示"验证码已过期,请重新获取"
验证码错误 400 提示"验证码错误剩余N次"
验证码错误超过3次 400 提示"验证失败次数过多,请重新获取"
验证码已使用 400 提示"验证码已使用,请重新获取"
发送频率超限60秒内重复发送 429 提示"发送过于频繁,请稍后再试"
每小时发送次数超限(>10次 429 提示"当前手机号发送次数超限,请稍后再试"
IP 请求频率超限 429 提示"请求过于频繁,请稍后再试"

10. 部署说明

  1. 在阿里云短信服务控制台创建签名和模板,获取 SignNameTemplateCode
  2. deploy/envs/user.env 中配置阿里云短信 AccessKey
# 阿里云短信配置(直接使用 AccessKey非 STS
SMS_ACCESS_KEY_ID=your_access_key_id
SMS_ACCESS_KEY_SECRET=your_access_key_secret
SMS_SIGN_NAME=TopFans
SMS_TEMPLATE_CODE=SMS_xxxxxxx
SMS_REGION=cn-hangzhou
  1. 重启 userService 服务

11. 安全注意事项

  1. 验证码日志脱敏:日志中禁止记录明文验证码,只能记录手机号和发送状态
  2. verify_token 安全token 有效期 5 分钟,只能使用一次,验证后立即删除
  3. 防暴力破解:验证失败 3 次后强制删除验证码,要求用户重新获取
  4. 限流保护:从手机号和 IP 两个维度限制请求频率

12. 待确定事项

  • 阿里云短信签名和模板CODE需在阿里云控制台创建
  • 验证码有效期(默认 60 秒是否合适)
  • 每小时同一手机号发送次数上限(默认 10 次)
  • 验证失败次数上限(默认 3 次后强制重新获取)