3. 注册流程生产实践

注册链路是账号体系的第一道生产关口。它同时承担四件事:把合法用户安全地写进用户表、把非法或机器人请求挡在门外、在弱网与重试下不产生重复账号、在失败时给出可恢复的路径。本节以手机号注册为主、邮箱注册为对照,给出可直接落地的接口设计与实现要点。

一、注册链路的整体数据流

一次手机号注册在服务端并不是“一个接口写一张表”,而是“验证码校验 → 风控判定 → 幂等占位 → 落库 → 发凭证”的串联。下图给出注册请求从客户端到数据库、缓存、风控与短信通道的完整数据流,以及每一步的失败分支。

客户端 手机号+验证码+密码 API 网关 限流 / 签名 / 设备指纹 注册服务 校验验证码 密码强度 / 风控 幂等占位 Redis 验证码 / 幂等键 MySQL user / user_auth 风控服务 IP/设备/频次 短信/邮件通道 发送验证码 失败分支与处理策略 验证码错误/过期 返回 4001,不消耗幂等键 密码强度不足 返回 4002,附具体规则 风控拦截 返回 4003,静默或二次验证 手机号已注册 返回 4009,引导登录 幂等键冲突 返回首次结果,不重复写库 DB 写入失败 释放幂等键,客户端可重试 说明:验证码校验通过后才占用幂等键;DB 失败必须释放幂等键,否则用户重试会被误判为重复注册。

二、接口设计与验证码校验

注册接口建议拆成两个:发送验证码与提交注册。发送验证码接口只负责生成 6 位数字码、写入 Redis 并调用短信通道;提交注册接口负责校验与落库。验证码在 Redis 中的键建议为 reg:code:{scene}:{phone},值为验证码,TTL 设为 300 秒,同时用 reg:code:limit:{phone} 记录发送次数,60 秒内只允许发送一次、24 小时内最多 10 次。

POST /api/v1/register/sms/send
{ "phone": "13800001234", "scene": "register" }

POST /api/v1/register/submit
{
  "phone": "13800001234",
  "code": "482913",
  "password": "Abc@123456",
  "deviceId": "d8f3...a1",
  "requestId": "req_20240612_9f2c"
}

校验验证码时要注意两点:一是“先比对、后删除”,比对成功立即删除键,防止同一验证码被重放;二是比对失败要累计错误次数,同一手机号连续错误 5 次后锁定 10 分钟,避免被暴力枚举。邮箱注册的差异在于验证码改为带签名的链接令牌,令牌中携带 email、exp、nonce,服务端用 HMAC 校验签名,避免在数据库中存储明文令牌。

三、密码强度策略

密码强度不应只做“长度 ≥ 8”这一条。生产上建议采用“基础规则 + 弱口令黑名单 + 相似度限制”三层策略。基础规则要求长度 8~32 位,且至少包含大写字母、小写字母、数字、特殊字符中的三类;弱口令黑名单拦截 12345678、password、qwerty123 等常见口令;相似度限制则拒绝与手机号、邮箱前缀、昵称高度相似的密码。

规则层判断条件失败返回
长度与字符集8~32 位,至少三类字符4002 + 缺失项
弱口令黑名单命中 Top 10 万弱口令库4002 + 建议更换
相似度与手机号/邮箱前缀编辑距离 ≤ 24002 + 提示

密码落库必须使用自适应哈希,推荐 bcrypt(cost=12)或 Argon2id。不要使用 MD5/SHA1,也不要在客户端做“可逆加密”后传输。服务端只存哈希值,登录时用同一算法比对。

四、防机器人注册

机器人注册的典型特征是:同一 IP 短时间大量请求、设备指纹重复、手机号段集中、验证码请求与提交间隔异常短。生产上建议组合使用四类手段:网关层按 IP 与设备指纹限流;发送验证码前要求通过图形验证码或行为验证;提交注册时校验“发送到提交”的时间差(例如小于 3 秒直接拒绝);对高风险请求引入二次验证(短信上行或人工审核)。风控判定结果应写入 risk_level 字段,低风险直接放行,中风险要求二次验证,高风险拒绝并记录设备指纹。

注意:风控规则不要只依赖单一维度。仅按 IP 限流容易被代理池绕过,仅按设备指纹又容易误伤多设备用户。建议至少组合“IP + 设备指纹 + 手机号段 + 时间间隔”四个维度。

五、注册幂等与失败重试

幂等的核心是“同一请求只产生一个账号”。实现方式是在提交注册时,用客户端传入的 requestId 或“手机号 + 场景”生成幂等键 reg:idem:{phone},通过 Redis SET NX EX 60 占位。占位成功才继续落库;占位失败说明已有相同请求在处理或已完成,此时应查询首次结果并原样返回,而不是再次写库。

落库阶段建议把 user 与 user_auth 放在同一本地事务中,先插入 user 拿到 user_id,再插入 user_auth 保存手机号与密码哈希。若事务失败,必须删除幂等键,否则用户重试会被误判为“重复注册”。客户端重试策略建议:网络超时重试 2 次,间隔 1 秒、3 秒;收到 4009(已注册)不重试,直接跳登录;收到 5001(服务繁忙)可重试,但需携带同一 requestId。

幂等键的 TTL 要略大于“最长处理时间 + 客户端重试窗口”。例如处理最长 5 秒、客户端重试窗口 30 秒,TTL 设为 60 秒较稳妥。TTL 过短会导致重试穿透,过长会占用 Redis 内存。

六、可直接使用的注册接口检查清单

  • 发送验证码:60 秒频控、24 小时上限、图形验证码前置、手机号格式校验。
  • 验证码校验:先比对后删除、错误计数、5 次锁定 10 分钟、TTL 300 秒。
  • 密码策略:长度 8~32、三类字符、弱口令黑名单、相似度限制、bcrypt cost=12。
  • 防机器人:IP + 设备指纹 + 手机号段 + 时间间隔四维组合,风险分级处理。
  • 幂等:SET NX EX 60 占位,失败返回首次结果,DB 失败释放幂等键。
  • 重试:超时重试 2 次,4009 不重试,5001 携带同一 requestId 重试。
  • 可观测:记录 requestId、phone 哈希、risk_level、耗时、失败码,便于排障与复盘。

把以上环节串起来,注册链路才算具备生产可用性:验证码保证“手机号真实可控”,密码策略保证“凭证强度”,风控保证“请求来自真人”,幂等与重试保证“弱网下不重复、不丢单”。这四者缺一不可,任何一环缺失都会在真实流量下暴露为重复账号、撞库或批量注册问题。