Files
shaguabijia-app-server/docs/integrations/sms.md
T
guke dfa3d4f07e 功能:创蓝云智(253)短信 provider(Mode B 自管码,可切换)
新增第三个可切换短信 provider chuanglan,与极光/阿里云并列(SMS_PROVIDER 切换,默认仍
jiguang,未知值回退 jiguang)。创蓝 tpl/send v2 是纯发送网关:本服务生成码、创蓝只下发、
本地内存校验(与极光同 Mode B)。存码/冷却/一次性/防爆破/GC 从极光隔离复制(极光文件不动,
零回归风险),唯一新逻辑是 HMAC-SHA256 签名(password 不上行)+ httpx POST + 错误码映射。
复用现有 httpx + 标准库 hashlib/hmac,无新依赖。

- app/integrations/sms/chuanglan.py: 新 provider
- app/integrations/sms/__init__.py: 分派器改 dict + chuanglan 路由
- app/core/config.py: SMS_PROVIDER 加 chuanglan + CHUANGLAN_SMS_* + chuanglan_sms_configured
- .env.example / docs/integrations/sms.md / docs/integrations/chuanglan/tpl-send.md: 配置与接口调研文档
- docs/superpowers/specs/2026-07-26-chuanglan-sms-verify-design.md: 设计 spec
- tests/test_sms_chuanglan.py(28 例)+ test_sms_dispatch.py 扩 chuanglan 路由

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-26 22:13:05 +08:00

5.9 KiB
Raw Blame History

短信验证码(sms

文件:app/integrations/sms/(分派器 __init__ + jiguang / aliyun provider + base) | 关联接口:auth-sms-send · auth-sms-login | ← 集成索引

作用

手机号 + 验证码登录的验证码发送 / 校验。支持可切换 provider(SMS_PROVIDER):jiguang(默认,极光自管码)/ aliyun(阿里云号码认证托管码)/ chuanglan(创蓝云智模板短信,自管码)。SMS_MOCK 另切 mock / real。

短信提供商(SMS_PROVIDER,可切换 + 灰度回退)

jiguang(默认) aliyun chuanglan
验证码模式 自管码 Mode B 托管码 Mode A 自管码 Mode B(与极光同)
验证码 本服务生成、极光只下发、本地内存校验 阿里云生成 + 下发 + 校验(dypns 号码认证,核验免费) 本服务生成、创蓝只下发、本地内存校验
发码 极光 /v1/messages SendSmsVerifyCode(##code## 占位) 创蓝 tpl/send v2(HMAC 签名头,password 不上行)
校验 比对本地存码 CheckSmsVerifyCodePASS / UNKNOWN 比对本地存码
多 worker ⚠️ 内存存码不共享(见已知局限) 阿里云托管,天然共享 ⚠️ 内存存码不共享(与极光同级债)
防爆破 单码失败 SMS_MAX_VERIFY_ATTEMPTS 次即作废 同语义(本地 per-phone 失败计数) 同语义(复制自极光)
单号冷却 本地 SMS_SEND_INTERVAL_SEC 交给阿里云 Interval 本地 SMS_SEND_INTERVAL_SEC
依赖 httpx(复用) SDK alibabacloud_dypnsapi20170525 httpx + 标准库 hashlib/hmac(无新依赖)
  • aliyun 需在号码认证控制台开通「融合认证」,用系统赠送签名 + 赠送模板;配置见 .env.exampleALIYUN_SMS_*,SDK 为 alibabacloud_dypnsapi20170525
  • chuanglanYZM 前缀验证码账号,服务器出网 IP 须在创蓝控制台加白名单(否则 117);Mode B 存码/冷却/校验逻辑是从极光隔离复制(极光文件不动),仅发送走 HMAC 签名。配置见 .env.exampleCHUANGLAN_SMS_*,接口调研见 chuanglan/tpl-send.md,设计见 spec

以下章节描述 jiguang provider(自管码)细节(chuanglan 的存码/冷却/校验语义与之相同)。

mock(SMS_MOCK=true,默认 / 开发测试) real(SMS_MOCK=false,生产)
发送 不真发,验证码打日志 调极光 /v1/messages 真发
校验 放行任意 6 位数字(测试便利) 比对本服务存的码(一次性 / 过期 / 防爆破)

为什么是"自己生成码"模式

极光有两种验证码玩法:① /v1/codes 极光生成 + /codes/{msg_id}/valid 极光校验;② /v1/messages 本服务生成码、极光只负责发。选 ②:校验本地完成(不依赖极光二次往返)、有效期/错误次数/作废完全可控、跟原 mock 口子结构一致。

函数 / 异常

函数 行为
send_code(phone) -> int 防刷检查(冷却)→ secrets 生成 N 位码 → 预占(冷却/存码)→ mock 打日志 / real 调极光 → 失败回滚预占。返回距下次可发秒数
verify_code(phone, code) -> bool mock 放行任意 6 位;real 比对存码,匹配即作废,失败累计到上限作废
SmsError(msg, status_code) status_code 决定 HTTP 码:过频 429、供应商失败 503、号码无效 400

配置

配置项 默认 说明
SMS_MOCK true mock / real 切换。生产置 false
SMS_CODE_TTL_SEC 300 验证码有效期(秒),与极光模板文案"5分钟"一致
SMS_SEND_INTERVAL_SEC 60 单号两次发送最小间隔(冷却)
SMS_SEND_ENDPOINT 极光 /v1/messages 短信发送地址
SMS_SIGN_ID 31729 极光签名 ID
SMS_TEMPLATE_ID 1 极光模板 ID(变量名 code)
SMS_CODE_LENGTH 6 验证码位数(本服务生成;前端 code 4-8 位兼容)
SMS_MAX_VERIFY_ATTEMPTS 5 单码最多校验失败次数,超过作废(防爆破)

鉴权复用极光一键登录:/v1/messagesbase64(JG_APP_KEY:JG_MASTER_SECRET) 做 HTTP Basic Auth——短信与一键登录是同一个极光应用(同 AppKey)。上线不需要额外凭证,只需 SMS_MOCK=false(JG_* 一键登录已配)。

防刷(短信花钱 + /sms/send 在登录前无法 JWT 鉴权)

2026-07-03 精简:登录风控只留「单号冷却 + 单设备频控」两道主策略(删单号每日上限、删登录纯 IP rate_limit);单码失败上限属验证码安全底线,保留。

  1. 单号 SMS_SEND_INTERVAL_SEC 冷却(单号维度)
  2. 单设备(device_id + IP)每小时频控:/sms/sendSMS_SEND_MAX_PER_HOUR_PER_DEVICE(5)、/sms/loginSMS_LOGIN_MAX_PER_HOUR(5)——堵「换号绕开单号冷却」+ 挡登录撞库,在 app/api/v1/auth.pyenforce_rate_limit
  3. 单码校验失败 SMS_MAX_VERIFY_ATTEMPTS 次即作废 + 验过即作废(一次性)
  4. 运维侧建议在极光控制台叠加:IP 白名单(只许服务器 IP)+ 防轰炸设置

极光错误码(节选,映射在 _send_via_jiguang)

code 含义 处理
50014 余额不足 logger.critical 告警 + 503
50009 极光侧超频 429
50006 手机号无效 400
其他 503

上线步骤

  1. 极光控制台:企业实名 + 短信充值 + 签名审核(sign_id=31729)+ 模板审核(temp_id=1)——均已就绪
  2. .envSMS_MOCK=false(JG_APP_KEY/JG_MASTER_SECRET 一键登录已配)
  3. 真机发一条验证:收到短信 + 能登录

已知局限

验证码存进程内存:单 worker uvicorn 够用;重启丢码(用户重发即可);多 worker / 多机不共享 → 冷却 / 校验失效,扩 worker 前迁移到 DB/Redis。见 待办与技术债