存量用户与业务身份映射
原则
网云穿用户原来的隧道、设备、套餐、余额/订单、权限等仍由网云穿数据库管理。本轮平台接入不能重新生成一套业务用户 ID,再把老用户当新用户处理。
- 平台长期身份键为
(issuer, sub)。 - 产品原用户 ID 作为不透明字符串使用,前导零和超出 JavaScript 安全整数范围的 ID 都不能丢失。
- 产品代码固定
wyc;服务端不能接受客户端提交任意产品代码来切换身份。 - 不迁移旧密码;已有手机号必须由业务方确认可信,正式导入需要显式确认。
- 一个手机号不代表可以自动合并同一产品的多个历史账号,冲突须隔离处理。
导入文件
UTF-8 CSV 固定表头:
product_code,external_user_id,phone_e164,display_name,source_created_at
wyc,000123456,+8613800000000,示例用户,2024-01-02T03:04:05+08:00
手机号采用 E.164;示例不是待导入的真实用户。先在隔离环境使用脱敏/合成数据演练,真实导出文件按个人信息保护要求保存,不提交 Git、不暴露在公开文档目录。
两阶段 CLI 操作
以下命令由平台运维执行,不是开放给产品任意写入映射的 HTTP API。
# 默认仅校验并生成脱敏报告
./ssoctl users import --file wyc-users.csv
# 核对 Dry Run 报告后,在正确的数据库配置下执行
./ssoctl users import --file wyc-users.csv \
--apply --trust-all-phones \
--sha256 SHA256_FROM_DRY_RUN
Dry Run 与 Apply 必须使用同一文件;文件变动需重新核对 SHA256。正式执行前备份、核对目标库,保留导入批次和报告。执行后复核成功数、冲突数与总数,抽查产品 ID 映射,并重复执行确认不会新增重复账号。
处理规则
| 输入情况 | 平台处理 |
|---|---|
| 合法、唯一手机号且不存在平台账号 | 创建 pending_activation 用户并预建映射 |
| 已有平台手机号 | 附加产品映射,仍须核对原绑定 |
| 同产品重复手机号 | 隔离冲突,不自动合并 |
| 同外部 ID 对应不同手机号 | 隔离冲突 |
| 空/非法手机号 | 隔离,不能导入成正常用户 |
| 同批次重跑 | 幂等续跑,不重复创建 |
预建用户没有密码,也没有被伪造的协议接受记录。首次仍须短信或微信认证、接受当前协议,之后才从 pending_activation 激活并继续原授权事务。
若某产品开启“强制绑定微信”,手机号预导入和短信认证仍不会伪造微信身份。用户必须实际完成公众号扫码,并验证预导入的原手机号后才可完成该产品授权;身份冲突继续进入隔离流程,不自动生成或合并账号。
产品回调如何绑定
Client 配置了 product_code=wyc 且用户存在该产品映射时,已验证的 ID Token / UserInfo 可能包含:
{
"sub": "00000000-0000-4000-8000-000000000001",
"tuyi_product_code": "wyc",
"tuyi_product_user_id": "000123456"
}
产品服务端按顺序处理:
- 先完整验证 OIDC 身份,不能仅解码 JWT,也不能相信客户端转述的 claims。
- 已有
(issuer, sub)绑定时,使用该业务用户;若与新声明的旧 ID 矛盾,拒绝并记录冲突。 - 无绑定但有本产品旧 ID 声明:查找对应老业务用户,检查未被另一个 sub 绑定;在数据库事务中加唯一约束后建立映射。
- 没有旧 ID 声明:不能直接推断这是新用户,也可能是未导入或导入冲突的老用户。迁移期先走核查/补全流程,再按产品明确的新注册策略处理。
- 绑定成功后用原业务用户 ID 创建产品会话,不改资产归属。
建议产品映射表至少对 (issuer, sub) 和本业务用户 ID 建立符合业务的一对一唯一约束;重复回调和并发首次登录只能建一条映射。
放量与回滚
先完成两次隔离演练,再内部账号、1%、10%、30%、100% 分阶段放量,每阶段观察业务高峰。旧登录入口及回退开关保留,出现映射错误立即停止放量,不在线执行自动账号合并。
手机号改变、禁用及会话撤销通过签名事件通知产品,但产品仍需撤销自己的会话。恢复演练需确认平台用户、映射和产品业务资产一致;不能只验证“数据库能启动”。