Files
2026-02-02 16:47:37 +08:00

152 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Client User Identity客户端用户标识建立用于 PUSH Token 绑定Spec
> 阶段高层规范spec
>
> 目标:在**无账号体系或账号可选**的前提下,为客户端生成一个稳定的“客户端用户标识”(下称 `client_user_id`),用于与 APNs/FCM 的 Push Token 建立绑定关系,便于后端精准下发 PUSH并支持 Token 变更/多设备/环境隔离等场景。
---
## 1. 背景与动机(摘要)
Push TokenAPNs device token / FCM registration token会发生变化重装、系统升级、重新授权、token rotate 等),且同一用户可能多设备。为了能稳定地“找到这个客户端实例/用户侧主体”并维护 Token 映射,需要一个**与 Token 解耦**、可持久化、不可推断的标识。
---
## 2. 目标Goals
- **建立 `client_user_id`**:客户端可生成并持久化一个稳定标识,作为后端 Push Token 绑定的主键之一。
- **Token 绑定可更新**:支持 Token 变更时“同一 `client_user_id` 重新上报即可更新”。
- **多设备兼容**:同一个账号(若未来引入)可关联多个 `client_user_id`;一个 `client_user_id` 可存在多个 Token例如同设备多渠道/多应用包形态)时需可扩展。
- **环境隔离**dev/prod、iOS/Android、bundle id / package name 维度隔离,避免串绑。
- **隐私友好**不使用可追踪的硬件标识IMEI/IDFA/Android ID 等),不引入额外合规风险。
---
## 3. 非目标Non-goals
- 不在本阶段引入完整账号体系、登录态、用户合并策略(如“同一人多设备合并为一个 user_id”
- 不在本阶段强制接入设备证明App Attest/Play Integrity仅在安全章节提出可选增强方向。
- 不定义具体数据库表结构与迁移脚本(属于 plan 阶段细化)。
---
## 4. 术语与对象Definitions
- **`client_user_id`**:客户端生成并持久化的随机标识,代表“一个客户端安装实例(或一段时间内的用户侧主体)”,用于 Push 绑定。
- **`push_token`**:系统/厂商下发的推送 tokeniOS/APNsAndroid/FCM可能变化。
- **`account_id`(可选)**:若未来存在登录账号,则用于把多个 `client_user_id` 归属到同一账号。
---
## 5. 关键决策:使用 UUID 做 `client_user_id` 是否合适?
结论:**合适**,推荐用**随机 UUIDUUID v4 为默认)**,并把它当作后端与客户端都不解析的**不透明字符串**。
### 5.1 为什么 UUID 合适
- **唯一性足够**v4 基于随机数,碰撞概率极低,满足全局唯一需求。
- **不可推断**:相较自增 ID不易被枚举相较设备硬件标识更隐私友好。
- **跨端易实现**iOS/Android/JS 都可稳定生成与序列化(字符串)。
### 5.2 需要明确的边界与注意事项
- **UUID 不等于“真实用户”**:它更像“安装实例 ID”。用户重装/清数据后可能变化;这对 Push 绑定通常可接受(新装产生新 token 与新 id
- **不要用设备硬件/系统可追踪 ID 替代**:避免隐私与合规风险,也避免系统限制导致的不稳定。
- **安全边界**:如果后端完全信任客户端上报的 `client_user_id`,存在“伪造绑定”风险;应结合登录态或签名/证明(见第 9 节)降低滥用。
### 5.3 UUID 版本建议
- **默认**UUID v4实现最简单、兼容最好
- **可选增强**UUID v7有时间有序性利于日志/索引与写入局部性),但需要确保两端实现一致与依赖可控。
---
## 6. 客户端行为规范Client Contract
### 6.1 生成与持久化
- 首次启动(或首次需要注册 Push 时):
- 若本地不存在 `client_user_id`:生成一个新的 UUID字符串写入持久化存储。
- 若已存在:直接复用。
- 存储建议(不做强约束,但必须“尽量稳定”):
- iOSKeychain
- AndroidKeystore 保护的加密存储/SharedPreferences或等价方案
- React Native/Expo使用安全存储能力例如 SecureStore/Keychain wrapper
### 6.2 Push Token 获取与上报时机
- 在以下任一时机触发“注册/更新”:
- 用户同意 Push 权限后获得 token
- App 冷启动获取到 token含 token 变更)
- 账号登录/登出(若存在账号)
- 环境切换dev/prod或应用更新可选
---
## 7. 后端接口契约API Contract摘要
> 具体路由/鉴权方式在 plan 阶段落地;此处先定义字段语义与幂等行为。
### 7.1 注册/更新绑定
- `POST /v1/push/register`
- 请求体(最小集):
- `client_user_id`: stringUUID 字符串)
- `platform`: `"ios" | "android"`
- `push_token`: string
- `app_id`: stringbundle id / package name用于隔离
- `env`: `"dev" | "prod"`
- (可选)`account_id`: string
- (可选)`device_meta`: `{ model, os_version, app_version, locale, timezone }`
- 行为要求(幂等):
-`push_token + env + app_id` 维度做唯一性约束,避免重复记录。
- 若同一 `client_user_id` 上报了新 token应更新/新增映射,旧 token 进入失效或保留历史(由实现决定,但必须可控)。
### 7.2 解绑(可选但建议)
- `POST /v1/push/unregister`
- 请求体:
- `client_user_id`
- `platform`
- `push_token`(或让后端按 `client_user_id` 批量解绑,二选一)
- `app_id`
- `env`
---
## 8. 数据模型(逻辑约束)
最小需要表达的关系:
- 一个 `client_user_id` 可对应 0..N 个 `push_token`考虑多端、多渠道、token rotate
- 一个 `push_token` 在同一 `env + app_id` 下应只对应一个“当前归属”(避免重复推送)。
- 若存在 `account_id`
- 一个 `account_id` 可关联 0..N 个 `client_user_id`(多设备)。
---
## 9. 安全与滥用防护(高层约束)
- **最小要求**:接口需具备基本鉴权与频率限制(例如基于设备指纹/匿名 session/应用侧签名的任一组合),避免被脚本批量绑定垃圾 token。
- **若存在登录态**:推荐绑定写入需要登录态(或在登录后把 `client_user_id` 归属到 `account_id`),降低“抢绑”风险。
- **可选增强(后续)**:接入 iOS App Attest / Android Play Integrity或对注册请求做一次性挑战签名。
---
## 10. 边界场景与处理原则
- **用户拒绝 Push 权限**:允许只有 `client_user_id`,不产生 token 绑定;后端不应报错。
- **token 变化**:客户端重新调用 `register`,后端必须幂等更新,避免重复推送。
- **重装/清数据**`client_user_id` 变化可接受;若未来有 `account_id`,可在登录后重新建立关联。
- **多环境**dev/prod token 不可混用;必须以 `env + app_id` 隔离。
---
## 11. 验收标准Acceptance Criteria
- 客户端能稳定生成并持久化 `client_user_id`(重复启动不变)。
- 在 token 获取/变更后,调用注册接口可在后端建立(或更新)绑定关系,且接口幂等。
- 同一 `push_token` 在同一 `env + app_id` 下不会产生多条“当前有效”绑定,避免重复推送。
- 在用户拒绝 Push 权限、无 token 的情况下,不影响 App 正常使用与后续再次授权后的绑定。