OpenNI文档
本页目录
第三方集成手机号与验证码

手机号与验证码登录

OpenNI 已实现中国大陆手机号登录:获取验证码、六位输入、重发冷却、过期处理、会话与账号注销。真实短信通过腾讯云发送,默认关闭;本地测试模式不会发送短信。完整接口和错误字段见 认证接口合同。

先在本机走通#

在仓库根目录启动隔离测试服务:

API_MODE=demo SMS_PROVIDER=test DATA_DIR=:memory: node services/api/src/server.mjs

把 App 或小程序连接到 http://localhost:8787/api/v1,进入手机号登录,输入测试手机号,再填接口返回的本地验证码。客户端应标明“本地验证码,未发送短信”。验证码每次随机生成,没有固定万能码。测试模式只允许 API_MODE=demo,不能用于生产;内存数据库随进程结束清空。

GET /api/v1/auth/capabilities 会返回可用登录方式。短信未配置时入口会显示不可用,不会创建假身份或假装发送短信。微信登录与短信登录可以分别启用。

测试验证码、手机号账号、重发状态和发送额度会记录验证来源。即使复用数据库和同一个 SMS_HASH_SECRET,测试账号与正式账号也保持独立:测试验证码不能换取正式会话,演示成员角色不会带入正式账号,也不会占用正式成员邀请。建议继续使用上面的独立内存数据库做本地测试。

准备腾讯云资料#

在自己的腾讯云账号中创建短信应用,完成国内短信签名和验证码模板审核,准备应用的 SDK AppID、签名正文、模板 ID、地域与有发送权限的 SecretId / SecretKey。国内短信签名应填写审核通过的文字,不是签名 ID;模板参数个数要与已审核模板一致。查看腾讯云 SendSms 官方文档。

本项目模板支持两种形式:一个变量放验证码;或第一个变量放验证码、第二个变量放有效分钟数 5。不要使用需要其他动态变量的模板。

从品牌配置导出服务器环境#

打开 品牌配置,展开可选的腾讯云短信配置,选择正式环境并填写资料。下载包中的 server.env 仅放服务器,不能放进公开的 club.json、网页、小程序或 Flutter 配置。

新部署可以生成一次 SMS_HASH_SECRET。已有数据库必须沿用原来的值。 它用于计算手机号身份索引和验证码摘要;更换它会让已有手机号无法对应原账号。把它与数据库备份一起安全保管,重新导出、迁移或重建服务器时不要生成新值。当前没有身份密钥轮换迁移工具。

也可手动复制根目录 .env.example 为 .env 并填写:

变量 填写内容
API_MODE production
SMS_PROVIDER tencent;默认 disabled
SMS_HASH_SECRET 一次生成、长期保管的至少 43 位 base64url 随机值
TENCENT_SMS_SECRET_ID / TENCENT_SMS_SECRET_KEY 服务器专用云 API 凭证
TENCENT_SMS_SDK_APP_ID 短信应用 SDK AppID
TENCENT_SMS_SIGN_NAME 审核通过的签名正文
TENCENT_SMS_TEMPLATE_ID 审核通过的验证码模板 ID
TENCENT_SMS_REGION 短信服务地域,例如 ap-guangzhou
TENCENT_SMS_TEMPLATE_HAS_MINUTES 一个变量填 false;验证码、分钟数两个变量填 true
SMS_TRUSTED_PROXY_IPS 可选,可信反向代理的精确 IP,逗号分隔

首次生成身份密钥的命令:

node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"

API 还要求品牌的 demoData=false,填写自己的经营主体、客服、隐私说明与服务协议。然后执行配置检查,再重启服务:

node scripts/doctor.mjs --env .env --mode production
node --env-file=.env services/api/src/server.mjs

配置检查不发送短信,也不验证腾讯云账号余额、模板权限或手机收取结果。自己的受控测试号码收到验证码后,还需验收错误码、重发、过期、重复使用和注销。此次实现只运行模拟供应商测试,没有发送真实短信。

验证码规则#

验证码有效 5 分钟,发送冷却 60 秒,同一挑战最多输错 5 次。按手机号最多每小时 5 次、每天 10 次;按 IP 最多每小时 20 次、每天 100 次。校验接口每个 IP 每 10 分钟最多 60 次。所有额度持久化,重启不会清空。

成功重发会替换之前未使用的验证码;供应商失败保留之前仍有效的码。超时不自动重发,因为供应商可能已经接受请求。真实接口的 delivery=accepted 只表示腾讯云接受请求,不能证明手机已收到。返回 retryAfterSeconds 时按服务器时长等待,不要循环重试。

默认不相信 X-Forwarded-For。若使用反向代理,SMS_TRUSTED_PROXY_IPS 只填写实际受控代理的精确 IP;代理须正确整理来源链。服务器从连接端向前跳过明确可信的节点,取第一个不可信来源。不要填通配符;配置缺失时代理后用户可能共享额度,应在上线前验证。

数据库只保存带服务端密钥的手机号、IP 和验证码摘要,不保存明文验证码或手机号登录索引。手机号仍会传给短信供应商完成发送,经营者应在自己的隐私说明中解释用途与供应商。发送额度记录保留滚动 24 小时,验证码记录过期 24 小时后在后续认证请求中清理;注销立即清除身份关联和该手机号挑战,额度记录仍按期限保留。备份也应遵守经营者的保留规则。

升级已有手机号数据库#

如果旧版本数据库没有记录短信验证来源,升级无法判断其中的手机号是否只用过本地测试码。系统会使这些账号的旧会话和未使用验证码失效,保留原用户、角色、微信关联与订单,不把未标记的手机号自动认作正式身份。

此后用真实验证码验证到这类旧手机号时,会提示“此手机号需要联系客服核实后登录”(SMS_IDENTITY_REVIEW_REQUIRED),不会静默创建新账号而隐藏旧订单。运营方需核实账号归属及成员邀请记录后处理恢复;当前没有自动恢复或合并工具。原有微信身份仍可通过正常微信登录访问原账号。升级前请先备份数据库与原 SMS_HASH_SECRET,不要靠更换密钥或删除账号来绕过核实。

手机账号如何微信付款#

短信登录只证明手机号,不生成虚假 OpenID。用户在订单页明确选择“关联微信并继续支付”后,客户端获取一次新的微信 code,用当前会话请求小程序 /auth/wechat/link 或原生 /auth/wechat-app/link。成功会保留同一个用户、角色和订单,仅增加相应微信身份并换发会话,再继续支付原订单。

微信身份已属于另一个账号时会拒绝关联,不会静默合并订单。一个账号对同一微信应用只关联一个身份;小程序和原生应用分别关联。仅有手机号登录的俱乐部可先验收预约,微信付款仍需要 对应应用和商户配置。新手机号账号只能是客户;成员角色仍由管理员邀请码授予。

常见问题#

提示 处理
短信登录尚未配置 检查 SMS_PROVIDER 与完整参数,重启 API
俱乐部尚未完成资料 应用真实品牌与政策配置,确认 demoData=false
请求未获确认 检查腾讯云签名、模板和额度;等待冷却,不连续点击
验证码过期或已被替换 使用最近一次获取的码;必要时等待后重新获取
尝试次数已用完 当前挑战已锁定,按提示等待后重取
此微信已关联其他账户 联系俱乐部客服,或明确切换账号;系统不会自动合并
此手机号需要联系客服核实后登录 联系俱乐部核实旧账号归属;不要反复获取验证码或改用新账号覆盖订单

供应商请求使用固定 HTTPS 地址与 TC3 签名方法,不自动重试;密钥和供应商原始错误不会返回客户端。手机验证码不能消除转售号码或 SIM 卡被接管的风险,高权限运营入口仍使用独立管理员凭证。

正在加载搜索索引…