Fine-Grained Auditing 在 ABP vNext 的落地
Fine-Grained Auditing 在 ABP vNext 的落地 🚀
📚 目录
- Fine-Grained Auditing 在 ABP vNext 的落地 🚀
-
- 0) 摘要(TL;DR)🧠
- 1) 问题边界与术语🎯
- 2) 总体架构 🧩
- 3) 审计账本 🔐
-
- 3.1 数据模型
- 3.2 并发安全:CAS + 显式锁(FOR UPDATE / UPDLOCK)
- 4) ABP 集成点(主体/多租户/替换审计)🧩
-
- 4.1 模块配置:开启 GET 审计 + DI 注册拦截器
- 4.2 查询处打 Tag(生产可关闭 CallSite)
- 4.3 替换 `IAuditingStore`(可靠接管读取审计)
- 5) EF Core 命令拦截器(异步 + 归一化指纹 + 有界通道)⚙️
-
- 5.1 有界通道注册(背压/降级策略)
- 5.2 拦截器实现(去注释/参数占位/字符串字面量/统一大小写)
- 6) 后台消费者:**批量事务** + **CAS 条件更新**(PG/SQL Server 双范式)📦
-
- 6.1 PostgreSQL 版本(Npgsql)
- 6.2 SQL Server 版本(System.Data.SqlClient)
- 7) 数据库审计与“指纹”🔎
-
- 7.1 PostgreSQL:pgAudit(含行数)+ queryid
- 7.2 SQL Server:Audit + query_hash
- 7.3 会话级上下文(稳妥对齐 TraceId/Tenant)
- 8) 可回放:还原“当时看到的内容”⏪
-
- 8.1 SQL Server Temporal
- 8.2 回放校验流程(与账本摘要比对)
- 9) 报表与合规模块 📊
- 10) 性能与高可用 ⚡
- 11) 评测方案 🧪
- 12) 三阶段路线 🛠️
- 13) 常见坑与规避 🧯
- 14) Checklist ✅
- 15) 附:契约与 DTO 📦
0) 摘要(TL;DR)🧠
我们在 ABP vNext 中落地细粒度访问审计:精准记录谁(用户/租户)在何时访问了何对象(实体/字段/记录/SQL),并能回放“当时看到的内容”与验真。
三层结构:
- 采集层:ABP 审计(含 GET)+ EF Core Query Tag + 异步命令拦截器(去注释/参数归一化得到稳定指纹);数据库侧启用 SQL Server Audit / PostgreSQL pgAudit(含行数)。
- 账本层:Append-only + 哈希链 + KMS/Key Vault 对“摘要”签名(可轮换/可验签)+ CAS 防断链;
- 回放层:SQL Server Temporal(或 PG 历史表/有效期区间)时间旅行查询 + 摘要比对。
原则:最小化敏感明文、可验证、可回放、工程可运维(有界队列、采样/白名单、分区归档)。✨
1) 问题边界与术语🎯
- “谁能看什么”(授权/RLS/DDM)≠ “谁看过什么”(访问审计)。本文聚焦访问审计与证据链(防抵赖 + 可回放)。
- “可回放”= 按账本时间点重建“当时视图”,并以摘要校验一致性。
2) 总体架构 🧩
3) 审计账本 🔐
3.1 数据模型
CREATE TABLE audit_ledger (id BIGSERIAL PRIMARY KEY,time_utc timestamptz NOT NULL,tenant_id uuid NULL,user_id uuid NULL,resource_type text NOT NULL,resource_key text NOT NULL,access_type text NOT NULL, -- 'READ','EXPORT','SELECT',...field_mask text NULL,row_count int NULL, -- 来自 DB 审计或回放query_fingerprint text NULL, -- PG queryid / SQL query_hash / 备用哈希data_digest bytea NULL, -- 可选:结果摘要/Merkle 叶prev_hash bytea NOT NULL,hash bytea NOT NULL, -- H(record_without_hash || prev_hash)key_version text NOT NULL,signature bytea NOT NULL
);-- 链尾单行表(CAS 尾指针)
CREATE TABLE audit_chain_tail (id int PRIMARY KEY,last_hash bytea NOT NULL
);
INSERT INTO audit_chain_tail(id, last_hash)
VALUES (1, decode('', 'hex')) ON CONFLICT DO NOTHING;