8.1 KiB
8.1 KiB
多端登录会话身份模型设计
日期:2026-04-21
1. 文档目的
这份文档用于补齐当前鉴权体系中“多端登录识别”能力的落地设计,目标是让后端能够稳定标识:
- 不同设备上的浏览器登录
- 同一设备上的不同浏览器登录
- 微信小程序、支付宝小程序、抖音小程序等小程序来源
- 微信内 H5 与普通浏览器 H5 的差异
同时冻结:
refresh_session需要新增的客户端身份字段- 前端与网关需要补传的上下文
- Axum 派生展示名与“当前设备”标识的规则
- 与后续
/api/auth/sessions、/api/auth/sessions/:sessionId/revoke的契约关系
2. 当前问题
当前项目里“设备类型”只有最小占位语义:
- Node
buildAuthRequestContext(...)直接写死clientType = "browser" clientLabel只是按User-Agent粗略显示“移动端浏览器 / 网页端浏览器”refresh_session/user_sessions里没有区分运行时、小程序来源、浏览器实例与设备实例的结构化字段
这会导致:
- 无法区分“同一台电脑上的 Chrome 和 Edge”
- 无法稳定标记“微信小程序登录”和“微信内 H5 登录”
- 无法给会话列表生成稳定、可读的端侧标签
3. 设计目标
新的会话客户端身份模型必须满足:
- 会话列表能区分“设备”与“客户端运行时”
- 同设备不同浏览器被视为不同登录端
- 小程序来源可稳定区分到平台级别
- 前端不需要拿到真实设备名称,也能展示稳定友好标签
- 浏览器拿不到真实设备名时,系统仍能自动生成展示名
4. 基本原则
4.1 不追求真实设备名
浏览器端通常无法拿到:
- 系统设置里的设备名称
- 手机自定义名称
- 电脑主机名
因此本设计不依赖真实设备名称,而是生成“会话展示名”。
4.2 会话识别拆三层
当前阶段统一拆为:
client_type大类,例如浏览器、小程序、桌面客户端client_runtime具体运行时,例如 Chrome、Safari、微信小程序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 |
否 | 平台,如 windows、macos、ios、android |
client_instance_id |
String |
否 | 客户端实例 ID,由前端持久化生成 |
device_fingerprint |
String |
否 | 后端派生指纹,用于聚合同设备近似会话 |
device_display_name |
String |
是 | 给用户展示的设备/端侧名称 |
mini_program_app_id |
Option<String> |
否 | 小程序 appid |
mini_program_env |
Option<String> |
否 | 小程序环境,如 develop、trial、release |
user_agent |
Option<String> |
否 | 原始 UA |
ip |
Option<String> |
否 | 登录 IP |
说明:
client_type和client_runtime是强约束字段,必须稳定可枚举。client_instance_id用于区分“同一台设备上的不同浏览器/不同安装实例”。device_fingerprint不是鉴权凭据,只用于会话展示聚类。device_display_name是最终展示字段。
6. 枚举定义
6.1 client_type
固定枚举:
web_browserwechat_h5mini_programnative_appdesktop_appunknown
6.2 client_runtime
当前阶段固定允许:
chromeedgesafarifirefoxwechat_embedded_browserwechat_mini_programalipay_mini_programdouyin_mini_programunknown
后续如扩展,不改已有语义,只追加枚举。
6.3 client_platform
当前阶段固定允许:
windowsmacoslinuxiosandroidunknown
7. 前端采集输入
7.1 Web 浏览器
浏览器侧建议补传以下请求头:
x-client-typex-client-runtimex-client-platformx-client-instance-id
其中:
x-client-instance-id由前端首次启动时生成并持久化到本地存储x-client-runtime可由前端粗判,也允许后端根据 UA 覆盖修正
7.2 小程序
小程序侧额外补传:
x-client-type=mini_programx-client-runtime如wechat_mini_programx-client-platformios/androidx-client-instance-idx-mini-program-app-idx-mini-program-env
8. 后端判定规则
8.1 输入优先级
Axum 侧按以下顺序解析:
- 显式请求头
User-Agent自动识别- 回退默认值
8.2 默认回退
如果没有任何显式端侧头:
client_type = web_browserclient_runtime由 UA 粗判,判不出则unknownclient_platform由 UA 粗判,判不出则unknown
8.3 微信内 H5
如果 UA 命中 MicroMessenger 且不是小程序显式来源:
client_type = wechat_h5client_runtime = wechat_embedded_browser
8.4 小程序来源优先于 UA
如果 x-client-type=mini_program:
- 不再按普通 UA 逻辑覆盖成浏览器
client_runtime必须优先保留小程序来源值
9. client_instance_id 规则
9.1 作用
它用于区分:
- 同设备不同浏览器
- 同浏览器不同安装实例
- 小程序容器与普通 H5 容器
9.2 生成策略
前端首次启动时生成一个随机字符串并持久化:
- Web:
localStorage/indexedDB - 小程序:平台侧本地存储
说明:
- 清浏览器数据后会变化,这是可接受的。
- 它不是登录凭据,只是会话识别辅助字段。
10. device_fingerprint 规则
后端可基于以下字段做稳定哈希:
client_typeclient_runtimeclient_platformclient_instance_id
如果 client_instance_id 缺失,则回退:
client_typeclient_runtimeclient_platformnormalized user_agent
说明:
- 这个指纹只用于显示和聚类,不作为鉴权依据。
- 同一设备上的不同浏览器通常应产生不同指纹。
11. device_display_name 派生规则
后端固定按以下优先级派生:
- 小程序:
微信小程序 / iPhone微信小程序 / Android支付宝小程序 / Android
- 微信内 H5:
微信内网页 / iPhone微信内网页 / Android
- 普通浏览器:
Windows / ChromemacOS / SafariiPhone / SafariAndroid / Chrome
- 无法识别时:
未知设备
12. 与会话列表 DTO 的关系
当前 AuthSessionSummary 至少建议新增:
clientRuntimeclientPlatformdeviceDisplayNameminiProgramAppId
当前已有字段中的:
clientType保留,但升级为新枚举clientLabel后续可由deviceDisplayName替代,或先兼容保留
13. 首版 Rust 落地范围
当前首版建议先完成:
- Axum 从请求头 + UA 解析
SessionClientContext module-auth的refresh_session增加结构化客户端字段- 登录创建 session 时写入这些字段
- 单元测试覆盖浏览器、小程序、微信 H5 的识别规则
本轮不强制一起完成:
/api/auth/sessions- 前端全面补传
x-client-*头 - 小程序端 SDK 对接
14. 不允许的设计漂移
后续实现时禁止:
- 继续把所有终端都写成
browser - 用
User-Agent文本直接充当最终设备名称 - 用 IP 作为设备唯一标识
- 把
client_instance_id当成安全凭据 - 前端任意上传自由文本设备名并直接持久化
15. 完成定义
满足以下条件时,这条能力设计视为完成:
- 会话身份字段已能区分来源、运行时、平台、实例
- 同设备不同浏览器与小程序来源都有明确建模
device_display_name已有统一派生规则- 后续
/api/auth/sessions已有稳定可用的数据基础