70 lines
6.9 KiB
Markdown
70 lines
6.9 KiB
Markdown
# 短信账户功能开发记录
|
||
|
||
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参数传入:
|
||
|
||
```php
|
||
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缓存已精确清理,归档及证据保留。该归档包含私密配置及历史失败诊断,不整体共享,不直接复跑已清理环境。
|
||
|
||
下一步在短信资料未齐时,可继续其他已授权功能;接入短信时复用本契约补供应商适配,不需要改动融资录入内容或产品机构库。
|