9.2 KiB
9.2 KiB
auth_audit_log 表设计
日期:2026-04-21
1. 文档目的
这份文档用于完成 M2 的第四条任务:设计 auth_audit_log。
目标是把鉴权审计表固定成一张“只追加的事实表”,并明确:
- 哪些鉴权动作必须写审计
- 审计表与风控表、会话表、账号表的边界
- 哪些字段应该入库,哪些字段应该在读取时派生
/api/auth/audit-logs要依赖什么读模型
2. 当前基线
当前 Node 后端已经存在 auth_audit_logs 表,并有完整写入链路:
authService.ts通过writeAuthAuditLog(...)统一写入GET /api/auth/audit-logs会按用户倒序返回最近 20 条- 前端拿到的是已经过展示加工的 DTO:
titledetailipMaskeduserAgentcreatedAt
当前 Node auth_audit_logs 表字段基线:
iduser_idevent_typedetailipuser_agentmeta_jsoncreated_at
当前已使用的事件类型基线:
password_loginphone_loginwechat_loginwechat_bind_phonechange_phonecaptcha_requiredlogoutlogout_allrevoke_sessionrisk_block_phonerisk_block_iprisk_unblock_phonerisk_unblock_ip
3. 表职责边界
3.1 auth_audit_log 负责
- 记录“账号已经发生过什么安全相关动作”
- 记录动作发生时的操作者账号
- 记录动作发生时的 IP、UA 与必要扩展上下文
- 作为
/api/auth/audit-logs的唯一事实来源
3.2 它不负责
- 风控封禁当前是否生效
- refresh session 当前是否活跃
- 短信验证码发送频控
- 账号当前状态
- 展示标题的最终本地化文案
3.3 与其他表的边界
auth_risk_block负责“当前拦截状态”auth_audit_log负责“发生过封禁或解除动作的历史事实”refresh_session负责“当前设备会话”auth_audit_log负责“谁在什么时候移除了哪台设备”
4. 访问级别
auth_audit_log 固定为 private table。
原因:
- 含用户行为安全记录
- 含原始 IP 与原始 UA
- 含 provider 或设备相关元信息
对外读取固定通过:
- Axum 鉴权后按当前
user_id查询 - 再由 view / service 转成脱敏 DTO
5. 字段设计
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
audit_log_id |
String |
是 | 主键,建议继续沿用 audit_* 前缀。 |
user_id |
String |
是 | 归属账号 ID,外键指向 user_account.user_id。 |
event_type |
String |
是 | 事件类型枚举。 |
detail |
String |
是 | 当前事件的人类可读详情,继续保留中文。 |
ip |
Option<String> |
否 | 事件发生时采集到的原始 IP。 |
user_agent |
Option<String> |
否 | 事件发生时采集到的原始 UA。 |
meta_json |
Option<String> |
否 | 扩展上下文 JSON 字符串,例如 sessionId、targetIp、expiresAt。 |
created_at |
String |
是 | 事件发生时间,UTC RFC3339。 |
补充约束:
- 当前阶段不增加
updated_at,因为审计事件一旦写入就不允许回写。 - 当前阶段不单独存
title,继续由event_type在读取时派生。 detail继续保留中文事实描述,避免后续只剩事件码却缺少可读上下文。
6. 事件类型设计
当前阶段 event_type 固定只支持以下值:
password_loginphone_loginwechat_loginwechat_bind_phonechange_phonecaptcha_requiredlogoutlogout_allrevoke_sessionrisk_block_phonerisk_block_iprisk_unblock_phonerisk_unblock_ip
当前不新增更细子类型,原因:
- 现有前端与 Node 展示已经围绕这 13 类建立
- M2 重点是兼容迁移,不是先重做一套新的安全事件 taxonomy
后续若要扩展,只允许追加,不重命名已有事件码。
7. 展示派生规则
7.1 title 不入库
/api/auth/audit-logs 返回的 title 固定按 event_type 派生。
当前派生规则与现有 Node 对齐:
password_login->账号密码登录phone_login->手机号登录wechat_login->微信登录wechat_bind_phone->绑定手机号change_phone->更换手机号captcha_required->需要图形验证码logout->退出当前设备logout_all->退出全部设备revoke_session->移除登录设备risk_block_phone->手机号临时保护risk_block_ip->网络临时保护risk_unblock_phone->解除手机号保护risk_unblock_ip->解除网络保护
7.2 ipMasked 不入库
原始 IP 继续入库,但对外返回时必须脱敏:
- IPv4 ->
a.b.*.* - IPv6 -> 保留前两段,其余隐藏
8. 唯一约束与索引
8.1 必须具备的唯一约束
audit_log_id主键唯一
8.2 必须具备的查询索引
(user_id, created_at DESC)作用:支撑/api/auth/audit-logs(user_id, event_type, created_at DESC)作用:后续按事件分类筛选或管理后台排查(created_at DESC)作用:后续归档与清理窗口扫描
9. 写入规则
9.1 登录相关
必须写入:
- 密码登录成功 ->
password_login - 手机号登录成功 ->
phone_login - 微信登录成功 ->
wechat_login - 微信绑定手机号成功 ->
wechat_bind_phone
9.2 手机号变更相关
必须写入:
- 更换手机号成功 ->
change_phone - 图形验证码被触发或校验失败 ->
captcha_required
说明:
captcha_required当前不是“成功动作”,而是一个安全门槛触发事件。- 继续沿用现有 Node 语义,不再拆成
captcha_challenge_issued和captcha_verify_failed两类。
9.3 会话相关
必须写入:
- 当前设备退出 ->
logout - 退出全部设备 ->
logout_all - 移除指定远端设备 ->
revoke_session
9.4 风控相关
必须写入:
- 手机号被封禁 ->
risk_block_phone - IP 被封禁 ->
risk_block_ip - 手机号封禁被解除 ->
risk_unblock_phone - IP 封禁被解除 ->
risk_unblock_ip
10. meta_json 约定
meta_json 当前只放扩展上下文,不放主字段副本。
10.1 推荐写入内容
revoke_sessionsessionIdtargetIptargetUserAgent
risk_block_phone/risk_block_ipscopeKeyexpiresAt
captcha_requiredscenephoneNumberMasked(如需要)
10.2 禁止写入内容
- 原始 refresh token
- 密码明文
- 短信验证码明文
- 微信 access token
11. 读取规则
11.1 /api/auth/audit-logs
当前阶段固定返回:
- 最近 20 条
- 按
created_at DESC - 只返回当前用户自己的审计记录
11.2 DTO 转换规则
对外 DTO AuthAuditLogEntry 固定为:
ideventTypetitledetailipMaskeduserAgentcreatedAt
其中:
id <- audit_log_idtitle <- event_type派生ipMasked <- ip脱敏后派生
12. 与当前 Node auth_audit_logs 的映射关系
| Node 列 | 新字段 | 迁移规则 |
|---|---|---|
id |
audit_log_id |
原样迁移。 |
user_id |
user_id |
原样迁移。 |
event_type |
event_type |
原样迁移。 |
detail |
detail |
原样迁移。 |
ip |
ip |
原样迁移。 |
user_agent |
user_agent |
原样迁移。 |
meta_json |
meta_json |
原样迁移。 |
created_at |
created_at |
原样迁移。 |
13. reducer / service 落地约束
13.1 module-auth reducer 层
必须至少具备:
append_auth_audit_log
说明:
- 审计表是典型追加型事实表,不需要复杂更新 reducer。
- 后续若做归档清理,再单独新增 maintenance reducer 或离线清理任务。
13.2 Axum 应用层
固定负责:
- 决定在哪个业务动作成功或触发门槛时写审计
- 组织
detail - 组织
meta_json - 读取时把
event_type转为title
14. 不允许的设计漂移
后续实现时禁止出现以下情况:
- 把
title直接入库,导致显示文案和事件真相耦合 - 为了省事复用
auth_audit_log做当前风险态判断 - 为了省事复用
auth_audit_log做短信频控统计 - 在审计表中记录原始 refresh token、验证码或密码明文
- 更新已有审计记录,而不是追加新事实
15. 本任务完成定义
当以下条件满足时,设计 auth_audit_log 视为完成:
- 审计表的职责边界已和会话表、风控表切开。
- 事件类型、字段、索引与读取 DTO 派生规则已明确。
/api/auth/audit-logs所需的查询语义已经固定。- 后续可以直接据此编码 reducer、view 与 Axum 读取逻辑。
16. 依据文件
server-node/src/auth/authService.tsserver-node/src/repositories/authAuditLogRepository.tsserver-node/src/routes/authRoutes.tsserver-node/src/db/migrations.tspackages/shared/src/contracts/auth.tsdocs/prd/MY_TAB_SETTINGS_AND_SECURITY_PRD_2026-04-16.mddocs/technical/SPACETIMEDB_AUTH_USER_ACCOUNT_TABLE_DESIGN_2026-04-21.mddocs/technical/SPACETIMEDB_AUTH_IDENTITY_TABLE_DESIGN_2026-04-21.mddocs/technical/SPACETIMEDB_REFRESH_SESSION_TABLE_DESIGN_2026-04-21.md