认证与会话
本文档详细说明 AuthNexus 的认证流程、Token 管理机制、会话生命周期以及多设备处理策略。
认证流程概览
AuthNexus 的认证流程是一个多层安全验证过程,从 TLS 传输层到业务层逐级校验。
完整认证时序
SDK Client server_app control_plane_app
│ │ │
├──── TCP 连接 ────────────────►│ │
│ │ │
├──── mTLS 握手 ──────────────►│ │
│ (CA 链 + EKU + SAN) │ │
│◄─── 握手完成 ────────────────┤ │
│ │ │
├──── 登录请求 ────────────────►│ │
│ (app_id + username │ │
│ + password) │ │
│ ├──── 密码验证(本地DB) ────────►│
│ │◄─── 验证结果 ────────────────┤
│ │ │
│ ├──── 签发 Token ──────────────│
│◄─── 登录成功 + Token ────────┤ │
│ │ │
├──── 业务请求(携带 Token) ───►│ │
│◄─── 响应 ────────────────────┤ │第一层:mTLS 握手
SDK 连接 server_app 时,TLS 握手承担四道约束验证:
- CA 信任链:客户端证书必须由受信的
app_client_ca签发 - 证书语义:证书包含
clientAuthEKU(Extended Key Usage) - 应用绑定:证书 SAN 中包含
urn:authnexus:app:<id>URI,标识所属应用 - CP 绑定:证书信息在
app_mtls_trust_bundle/app_cert_bindings中注册
TIP
mTLS 不仅验证身份,还在传输层建立了应用隔离边界。证书中的 app_id 与后续登录请求中的 claimed app_id 必须一致。
第二层:OCSP Stapling 验证
如果节点证书标记了 must-staple:
- 节点在 TLS 握手时附带 OCSP Response(从 Channel 4 获取)
- SDK 验证 OCSP Response 签名(使用
ocsp_signer_ca.pem)、时间窗口(maxsec=1800s)与证书状态 - 无有效 staple 时 SDK 直接拒绝握手
第三层:业务登录
mTLS 握手成功后,SDK 发送登录请求(自定义二进制协议,非 HTTP):
// SDK 侧(伪代码)
AuthRequest request;
request.app_id = 1001;
request.username = "user@example.com";
request.password = "user_password";
auto result = sdk.login(request);服务端验证流程:
- 确认
claimed app_id与 mTLS 证书中的app_id一致 - 在本地 Runtime DB 中查找用户
- 使用 HMAC-SHA256 验证密码哈希
- 检查用户状态(是否被禁用、黑名单等)
- 检查当前认证 Epoch 是否有效
- 签发 Token 并创建会话
Token 管理
Token 生成
AuthNexus 使用高速版本化 HMAC-SHA256 Token 方案:
- 格式:
$authnexus-fast-hmac-sha256$v=1$<salt_hex>$<hash_hex> - Salt:16 字节随机数
- Hash:32 字节 HMAC-SHA256
- 域分离:使用
AuthNexusPasswordFastHashV1字符串
设计取舍
选择 HMAC-SHA256 而非 Argon2/bcrypt/scrypt 是有意为之的设计决策。AuthNexus 优先保证在线高吞吐与低延迟(验证耗时 < 0.5ms),而非离线抗暴力破解。业务场景中的安全性由 mTLS + OCSP + Epoch 等多层机制共同保障。
Token 验证
Token 验证在 server_app 本地完成,不需要访问 CP:
- 验证耗时 < 0.5ms
- 在
crypto_threads线程池中执行,不阻塞 IO 或 Logic 线程 - 验证失败立即断开连接
会话生命周期
会话创建
登录成功后创建会话对象,包含:
- 用户 ID 与应用 ID
- Token
- 创建时间与过期时间
- 客户端 IP 与设备信息
- 当前认证 Epoch
心跳维持
SDK 内置心跳线程自动维持会话活性:
SDK (heartbeat_thread) ──── 心跳包 ────► server_app
◄──── 心跳响应 ────- 心跳间隔可配置(通过
server_runtime_settings) - 服务端更新会话最后活跃时间
- 连续多次心跳超时视为会话断开
会话超时与清理
| 超时类型 | 默认行为 | 触发条件 |
|---|---|---|
| 空闲超时 | 断开连接 | 超过指定时间无心跳 |
| 绝对超时 | 强制注销 | 会话总时长超过上限 |
| Token 过期 | 拒绝请求 | Token 的有效期已过 |
会话清理由 TaskScheduler(BackgroundRuntime 中的后台服务)定期执行,清除过期会话并释放资源。
会话状态安全性
Session 状态 mutation 在任意线程都安全(Session 内部 mutex 保护)。单个 session 的串行访问由 connection.h 中 read_loop 的 await dispatch 自然保证。
多设备会话处理
同一用户可以从多个设备同时登录,每个设备独立维护会话:
- 每个连接对应独立的 Session 对象
- Token 不在设备间共享
- 应用策略(
app_policy)可配置单用户最大同时在线设备数 - 超出设备数限制时,最早的会话会被踢出
认证 Epoch 机制
Epoch 是 AuthNexus 的全局会话失效机制,用于紧急安全场景。
Epoch 工作原理
管理员触发 Epoch Bump
│
▼
Control Plane 更新 Epoch 值
│
├──── Channel ② SSE hint: epoch.bumped ────► server_app
│ │
│ ▼
├──── Channel ③ Delta Puller 拉取 ───────► auth_epoch_changes
│
▼
所有旧 Epoch 会话失效
用户需重新登录Epoch 应用场景
- 密钥泄露:怀疑 Token 签名密钥被泄露时,bump epoch 立即失效所有会话
- 安全事件:检测到大规模异常登录时,强制全员重新认证
- 系统维护:需要所有客户端重新建立连接时
Epoch 同步机制
Epoch 变更通过两条路径同步到节点:
- 快路径:Channel 2 SSE 推送
epoch.bumpedhint,节点立即拉取 - 慢路径:Channel 3 Delta Puller 定期拉取
auth_epoch_changes表
两条路径互为备份,SSE 健康时 5s/30s 间隔,SSE 降级时 Delta Puller 切换到 800ms/5000ms 快速轮询。
用户与应用的关系
首次卡密登录绑定
用户与应用的归属关系在 首次卡密登录 时建立,而非最后充值时:
- 用户首次使用某应用的卡密成功登录后,该用户归属于该应用
- 归属关系一旦建立不可变更
- 此设计确保代理佣金计算的确定性
多租户隔离
应用作为租户边界,不同应用的用户完全隔离:
- 用户数据按
app_id分区 - Token 仅在对应应用上下文中有效
- 跨应用访问在协议层被拒绝(mTLS 证书 SAN 校验)
平台管理员 vs 应用代理
两类角色的认证存在差异:
| 特性 | 平台管理员 (platform_admin) | 应用代理 (app_agent) |
|---|---|---|
agents.kind | platform_admin | app_agent |
app_id | NULL(全局) | 引用真实 application |
| 登录入口 | Admin 后台 | Admin 后台 |
| 权限范围 | 全平台所有应用 | 仅授权应用集合 |
| 应用绑定 | 无 | agent_app_grants 表 |
| 认证方式 | Admin API Token | Admin API Token |
平台管理员通过 app_id IS NULL 标识,应用代理通过 agent_app_grants 多对多关系绑定到一个或多个应用。Schema 层的 CHECK 约束 + FK ON DELETE RESTRICT + partial UNIQUE 三道防线确保数据一致性。
黑名单机制
服务端维护本地黑名单,用于即时阻断:
- 黑名单以本地 DB 为单一真相
- 通过 Delta Puller(Channel 3)从 CP 同步
server_blacklist_changes - 黑名单检查在登录与请求处理的 fast path 上执行
- 命中黑名单的连接立即断开