Files
airz-uniapp/docs/短信账户功能开发-20261012.md
T
hujun c95482a0f3 feat: 实现协议全文与同意记录功能
新增协议全文后台管理、版本化发布及本人同意记录功能,支持可撤销授权;完善资料与授权相关边界提示,调整评估提交时的资料与授权校验逻辑,同步更新相关页面文案与路由。
2026-10-14 14:53:13 +08:00

6.9 KiB
Raw Blame History

短信账户功能开发记录

2026-10-12,按用户“先写相关代码”的要求完成短信登录/首次注册、登录后验证码改密及原/新手机号双验证换绑的后端实现。供应商、已审核签名及模板暂缺,配置默认关闭;本轮未发送真实短信、连接生产或部署,未提交/推送。

现有小程序页面和请求契约可复用,本轮没有修改前端运行代码、融资评估28项输入或同事负责的产品机构库。已回撤的产品AI条件分析与人工复核不恢复。AUTH-001真实渠道仍阻塞,AUTH-002/004内部代码已接通,完整任务和正式验收仍未完成。

已实现行为

  • 验证码采用六位安全随机数字、300秒有效期、同号码60秒冷却;每小时号码最多10次、IP最多30次,错误最多5次。Redis仅保存HMAC摘要,号码、IP和用途/账户上下文的键也作摘要处理。
  • 登录验证码绑定登录用途和号码;安全验证码绑定用途、本人账户及发送时的当前手机号,不能跨账户、用途或换绑后的身份复用。
  • 发送先预留,供应商明确确认成功后才激活;失败或结果未知时不产生可用验证码,冷却和限额仍保留。迟到失败不能删除后续新挑战。
  • 单码/双码通过Lua原子核销;换绑任一码错误不消耗另一正确码,重复/并发最多成功一次。已核销后SQL、Hook或Token存储失败不恢复验证码,须重新发送。
  • 首次短信登录生成随机未知初始密码,沿用现有密码哈希、Token及注册逻辑;邮箱为空、昵称为“融资用户”、积分为零。已有正常账户直接登录,禁用账户拒绝。
  • 验证码改密须登录本人,不新增匿名找回密码;新密码8–32位且含字母和数字、不含空格。改密和换绑均撤销本人全部会话,要求重新登录。
  • 换绑检查同号码及号码占用,复用现有手机号唯一索引,无DDL或迁移;账户行锁内再次核对会话和当前号码,历史评估快照不改写。密码登录也在行锁内重核号码,防止换绑后的旧号码并发重建会话。

接口

路径均以 /api/v1 为前缀,请求为JSON;安全操作使用已有Bearer鉴权。

方法/路径 请求字段 行为
POST /auth/sms-code phone、purpose: login 发送登录验证码,成功返回 retryAfter/expiresIn
POST /auth/sms-login phone、code 登录/首次注册,返回原会话及profile契约
POST /account/security/sms-code purpose: password / phone-current / phone-new、newPhone 前两种只发给本人当前号码;新号码用途须提供有效且未占用的新号码
PUT /account/password verification: sms、newPassword、code,兼容页面发送的空 oldPassword 本人短信验证改密;原密码验证分支保留
PUT /account/phone newPhone、currentCode、newCode 原/新号码同时验证后换绑

改密/换绑成功返回 reLoginRequired: true。短信未启用或缺少可用Provider时,认证后的请求及公开短信入口返回503/SMS_NOT_CONFIGURED;未登录的安全操作仍返回401。关闭配置在供应商Hook及短信Redis连接之前生效,不发送、不生成验证码、不注册账户;原密码登录、资料及头像功能可继续使用。

后续供应商接入

后端仓库为 D:/AAdesk/202609/airz,新增服务 application/common/library/SmsVerification.php 和 AccountSms.php;application/extra/sms_verification.php 中的 enabled 固定为 false。

供应商实现以下契约,通过正常扩展Hook sms_verification_provider 返回Provider实例,配置项作为Hook参数传入:

namespace app\common\library\smsverification;

interface Provider
{
    public function available(): bool;
    public function send(string $phone, string $code, string $purpose): bool;
}

available() 只在凭据、已审核签名、用途模板均可用时返回true;send() 按四种用途映射模板并限制超时,只有供应商明确接受该次发送才返回true。异常、超时或未知结果不能当成功,也不能自动重试未知发送。日志须屏蔽号码、验证码和凭据;密钥通过环境配置注入,不存仓库。短信摘要秘密由已有服务端 token.key 派生,复用Token的Redis配置和独立 tokenprefix + sms: 命名空间。

缺少Provider时默认使用 UnavailableProvider。当前没有真实供应商实现、生产模拟发送开关或固定验证码;测试替身只在tests目录中、显式隔离bootstrap下使用。资料齐备后补Provider适配及真实渠道/目标端验证,再单独安排后端发布;本轮代码完成不等于短信已经上线。

验证证据

全新本机MySQL5.7.26/Redis DB15、26张空合成表、独立ROOT及命名空间,未读取默认生产 .env。四套最终通过结果如下,不重复累计中途运行及专项检查:

入口 最终回执 检查数 本机HTTP请求
tests/sms_verification.php sms-green-1 71 0
tests/account_sms.php account-green-4 271 54
tests/api_session.php session-regression-1 284 70
tests/api_profile.php profile-regression-1 268 76
合计 四套入口 894 200

覆盖关闭零写入、限流/过期/错误上限、摘要存储、发送失败、用途/账户隔离、重复及并发核销、首次注册、双码换绑、全会话撤销、禁用账户、Hook/SQL/Redis故障及原密码登录/资料/头像回归。旧号码并发登录缺陷已通过红测试复现并修正;独立专项27项不另计入上表。新增双worker竞争验证一次注册、号码唯一归属、赢家Token撤销及输家号码/Token保持;未强制两方同时通过占用预检,不宣称确定覆盖唯一索引冲突分支。独立只读复审未发现P1/P2问题。

本轮12个PHP文件语法检查和两仓diff检查通过。未改前端运行代码,未重新编译小程序;真实短信到达、渠道错误码/超时、平台及多设备流程仍待供应商接入后验证。

私密归档:D:/AAdesk/202609/airz-backups/account-sms/1791773313671。脱敏测试输出及单套JSON、verification.json、cleanup.json 和进程退出回执保留。每套测试后表行和Redis核空、结构及8原触发器保持;最终仅删除本轮精确合成数据库,关闭所属两服务并释放端口。395份屏蔽回执显示真实模型客户端加载0次;短信测试JSON夹具和本轮生成Python缓存已精确清理,归档及证据保留。该归档包含私密配置及历史失败诊断,不整体共享,不直接复跑已清理环境。

下一步在短信资料未齐时,可继续其他已授权功能;接入短信时复用本契约补供应商适配,不需要改动融资录入内容或产品机构库。