Files
Genarrative/docs/technical/MULTI_DEVICE_SESSION_IDENTITY_DESIGN_2026-04-21.md
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

8.1 KiB
Raw Permalink Blame History

多端登录会话身份模型设计

日期:2026-04-21

1. 文档目的

这份文档用于补齐当前鉴权体系中“多端登录识别”能力的落地设计,目标是让后端能够稳定标识:

  1. 不同设备上的浏览器登录
  2. 同一设备上的不同浏览器登录
  3. 微信小程序、支付宝小程序、抖音小程序等小程序来源
  4. 微信内 H5 与普通浏览器 H5 的差异

同时冻结:

  1. refresh_session 需要新增的客户端身份字段
  2. 前端与网关需要补传的上下文
  3. Axum 派生展示名与“当前设备”标识的规则
  4. 与后续 /api/auth/sessions/api/auth/sessions/:sessionId/revoke 的契约关系

2. 当前问题

当前项目里“设备类型”只有最小占位语义:

  1. Node buildAuthRequestContext(...) 直接写死 clientType = "browser"
  2. clientLabel 只是按 User-Agent 粗略显示“移动端浏览器 / 网页端浏览器”
  3. refresh_session / user_sessions 里没有区分运行时、小程序来源、浏览器实例与设备实例的结构化字段

这会导致:

  1. 无法区分“同一台电脑上的 Chrome 和 Edge”
  2. 无法稳定标记“微信小程序登录”和“微信内 H5 登录”
  3. 无法给会话列表生成稳定、可读的端侧标签

3. 设计目标

新的会话客户端身份模型必须满足:

  1. 会话列表能区分“设备”与“客户端运行时”
  2. 同设备不同浏览器被视为不同登录端
  3. 小程序来源可稳定区分到平台级别
  4. 前端不需要拿到真实设备名称,也能展示稳定友好标签
  5. 浏览器拿不到真实设备名时,系统仍能自动生成展示名

4. 基本原则

4.1 不追求真实设备名

浏览器端通常无法拿到:

  1. 系统设置里的设备名称
  2. 手机自定义名称
  3. 电脑主机名

因此本设计不依赖真实设备名称,而是生成“会话展示名”。

4.2 会话识别拆三层

当前阶段统一拆为:

  1. client_type 大类,例如浏览器、小程序、桌面客户端
  2. client_runtime 具体运行时,例如 Chrome、Safari、微信小程序
  3. client_instance_id 客户端实例 ID用于区分“同设备不同浏览器”以及“同浏览器不同安装/清缓存”

4.3 展示名由后端派生

device_display_name 不直接信任前端自由文本,而由后端基于结构化字段派生,必要时允许前端提供候选值。

5. 字段模型

refresh_session 建议新增以下字段:

字段名 类型 必填 说明
client_type String 大类,见 6.1
client_runtime String 运行时,见 6.2
client_platform String 平台,如 windowsmacosiosandroid
client_instance_id String 客户端实例 ID由前端持久化生成
device_fingerprint String 后端派生指纹,用于聚合同设备近似会话
device_display_name String 给用户展示的设备/端侧名称
mini_program_app_id Option<String> 小程序 appid
mini_program_env Option<String> 小程序环境,如 developtrialrelease
user_agent Option<String> 原始 UA
ip Option<String> 登录 IP

说明:

  1. client_typeclient_runtime 是强约束字段,必须稳定可枚举。
  2. client_instance_id 用于区分“同一台设备上的不同浏览器/不同安装实例”。
  3. device_fingerprint 不是鉴权凭据,只用于会话展示聚类。
  4. device_display_name 是最终展示字段。

6. 枚举定义

6.1 client_type

固定枚举:

  1. web_browser
  2. wechat_h5
  3. mini_program
  4. native_app
  5. desktop_app
  6. unknown

6.2 client_runtime

当前阶段固定允许:

  1. chrome
  2. edge
  3. safari
  4. firefox
  5. wechat_embedded_browser
  6. wechat_mini_program
  7. alipay_mini_program
  8. douyin_mini_program
  9. unknown

后续如扩展,不改已有语义,只追加枚举。

6.3 client_platform

当前阶段固定允许:

  1. windows
  2. macos
  3. linux
  4. ios
  5. android
  6. unknown

7. 前端采集输入

7.1 Web 浏览器

浏览器侧建议补传以下请求头:

  1. x-client-type
  2. x-client-runtime
  3. x-client-platform
  4. x-client-instance-id

其中:

  1. x-client-instance-id 由前端首次启动时生成并持久化到本地存储
  2. x-client-runtime 可由前端粗判,也允许后端根据 UA 覆盖修正

7.2 小程序

小程序侧额外补传:

  1. x-client-type=mini_program
  2. x-client-runtimewechat_mini_program
  3. x-client-platform ios / android
  4. x-client-instance-id
  5. x-mini-program-app-id
  6. x-mini-program-env

8. 后端判定规则

8.1 输入优先级

Axum 侧按以下顺序解析:

  1. 显式请求头
  2. User-Agent 自动识别
  3. 回退默认值

8.2 默认回退

如果没有任何显式端侧头:

  1. client_type = web_browser
  2. client_runtime 由 UA 粗判,判不出则 unknown
  3. client_platform 由 UA 粗判,判不出则 unknown

8.3 微信内 H5

如果 UA 命中 MicroMessenger 且不是小程序显式来源:

  1. client_type = wechat_h5
  2. client_runtime = wechat_embedded_browser

8.4 小程序来源优先于 UA

如果 x-client-type=mini_program

  1. 不再按普通 UA 逻辑覆盖成浏览器
  2. client_runtime 必须优先保留小程序来源值

9. client_instance_id 规则

9.1 作用

它用于区分:

  1. 同设备不同浏览器
  2. 同浏览器不同安装实例
  3. 小程序容器与普通 H5 容器

9.2 生成策略

前端首次启动时生成一个随机字符串并持久化:

  1. WeblocalStorage / indexedDB
  2. 小程序:平台侧本地存储

说明:

  1. 清浏览器数据后会变化,这是可接受的。
  2. 它不是登录凭据,只是会话识别辅助字段。

10. device_fingerprint 规则

后端可基于以下字段做稳定哈希:

  1. client_type
  2. client_runtime
  3. client_platform
  4. client_instance_id

如果 client_instance_id 缺失,则回退:

  1. client_type
  2. client_runtime
  3. client_platform
  4. normalized user_agent

说明:

  1. 这个指纹只用于显示和聚类,不作为鉴权依据。
  2. 同一设备上的不同浏览器通常应产生不同指纹。

11. device_display_name 派生规则

后端固定按以下优先级派生:

  1. 小程序:
    • 微信小程序 / iPhone
    • 微信小程序 / Android
    • 支付宝小程序 / Android
  2. 微信内 H5
    • 微信内网页 / iPhone
    • 微信内网页 / Android
  3. 普通浏览器:
    • Windows / Chrome
    • macOS / Safari
    • iPhone / Safari
    • Android / Chrome
  4. 无法识别时:
    • 未知设备

12. 与会话列表 DTO 的关系

当前 AuthSessionSummary 至少建议新增:

  1. clientRuntime
  2. clientPlatform
  3. deviceDisplayName
  4. miniProgramAppId

当前已有字段中的:

  1. clientType 保留,但升级为新枚举
  2. clientLabel 后续可由 deviceDisplayName 替代,或先兼容保留

13. 首版 Rust 落地范围

当前首版建议先完成:

  1. Axum 从请求头 + UA 解析 SessionClientContext
  2. module-authrefresh_session 增加结构化客户端字段
  3. 登录创建 session 时写入这些字段
  4. 单元测试覆盖浏览器、小程序、微信 H5 的识别规则

本轮不强制一起完成:

  1. /api/auth/sessions
  2. 前端全面补传 x-client-*
  3. 小程序端 SDK 对接

14. 不允许的设计漂移

后续实现时禁止:

  1. 继续把所有终端都写成 browser
  2. User-Agent 文本直接充当最终设备名称
  3. 用 IP 作为设备唯一标识
  4. client_instance_id 当成安全凭据
  5. 前端任意上传自由文本设备名并直接持久化

15. 完成定义

满足以下条件时,这条能力设计视为完成:

  1. 会话身份字段已能区分来源、运行时、平台、实例
  2. 同设备不同浏览器与小程序来源都有明确建模
  3. device_display_name 已有统一派生规则
  4. 后续 /api/auth/sessions 已有稳定可用的数据基础