安全审计(Aegis.Security.Audit)
Aegis.Security.Audit 用来记录业务操作留痕。它负责注册 AuditLogger<T> 和默认审计存储,让业务服务或控制器可以用链式写法记录谁做了什么、影响了什么资源、变更了什么数据。
组件概览
| 字段 | 说明 |
|---|---|
| 组件名称 | 安全审计 |
| 真实类库 | Aegis.Security.Audit |
| 组件定位 | 操作留痕与审计日志组件 |
| 引入方式 | 安装 NuGet,并在 Component.deps.json 的 Services 中启用 Security.Audit |
| 组件声明 | Security.Audit |
| 核心能力 | AuditLogger<T>、链式审计构建器、IAuditStorage |
| 默认存储 | AuditConsoleStorage |
什么时候要用它
适合场景:
- 需要记录敏感操作、关键业务操作
- 需要做合规审计和问题追溯
- 需要保留“谁在什么时间改了什么”的证据链
最小可运行路径
第一步:启用 Security.Audit
{
"Components": {
"Services": [
"Security.Audit"
],
"Middlewares": []
}
}
第二步:注入 AuditLogger<T>
public class SettlementService
{
private readonly AuditLogger<SettlementService> _auditLogger;
public SettlementService(AuditLogger<SettlementService> auditLogger)
{
_auditLogger = auditLogger;
}
}
第三步:直接写审计日志
await _auditLogger
.SetOperator("1", OperatorType.医生, "张三")
.SetDomain(Domain.结算单)
.SetContent(OperationType.创建, "SETTLE_001", "创建结算单", "创建结算单 SETTLE_001")
.LogAsync();
默认帮你注册了什么
启用组件后,框架会默认注册:
AuditLogger<T>:ScopedIAuditStorage:默认是控制台存储
这意味着当前最小接入可以先跑起来,但正式项目通常还需要替换默认存储。
正式项目最常见的扩展点
替换默认存储
如果你希望把审计数据写入数据库、消息队列或其他介质,可以实现 IAuditStorage。
public class AuditDatabaseStorage : IAuditStorage
{
private readonly IFreeSql<AegisDb> _freeSql;
public AuditDatabaseStorage(IFreeSql<AegisDb> freeSql)
{
_freeSql = freeSql;
}
public void Create(AuditRecord auditRecord)
{
_freeSql.Insert(auditRecord).ExecuteAffrows();
}
public Task CreateAsync(AuditRecord auditRecord)
{
return _freeSql.Insert(auditRecord).ExecuteAffrowsAsync();
}
}
在项目里覆盖注册
services.AddScoped<IAuditStorage, AuditDatabaseStorage>();
最常见的三种记录方式
普通操作记录
await _auditLogger
.SetOperator("1", OperatorType.医生, "张三")
.SetDomain(Domain.结算单)
.SetContent(OperationType.创建, "创建结算单", "创建结算单 SETTLE_001")
.LogAsync();
带资源标识的记录
await _auditLogger
.SetOperator("1", OperatorType.医生, "张三")
.SetDomain(Domain.结算单)
.SetContent(OperationType.更新, "SETTLE_001", "更新结算单", "修改结算单金额")
.LogAsync();
带数据变更的记录
await _auditLogger
.SetOperator("1", OperatorType.医生, "张三")
.SetDomain(Domain.结算单)
.SetContent(OperationType.更新, "SETTLE_001", "更新结算单", "修改结算单金额")
.SetChangeData("金额,状态", newData, oldData)
.LogAsync();
PreSetOperator 什么时候用
如果一个服务里的审计操作者几乎总是“当前登录用户”,可以先预设操作者,再在各个方法里直接写领域和内容。
public class SettlementService
{
private readonly AuditLogger<SettlementService> _auditLogger;
public SettlementService(AuditLogger<SettlementService> auditLogger)
{
_auditLogger = auditLogger;
_auditLogger.PreSetOperator("1", OperatorType.医生, "张三");
}
public async Task CreateAsync()
{
await _auditLogger
.SetDomain(Domain.结算单)
.SetContent(OperationType.创建, "SETTLE_001", "创建结算单", "创建结算单 SETTLE_001")
.LogAsync();
}
}
接入后怎么确认已经生效
可以这样确认:
- 业务类里可以正常注入
AuditLogger<T> - 调用
Log()或LogAsync()后没有构建异常 - 默认控制台能看到输出,或者自定义存储能拿到记录
常见问题
为什么 SetDomain(...) 直接报错
通常是因为你走的是预设操作者模式,但没有先调用 PreSetOperator(...)。
正式项目能不能直接用默认存储
不建议。
默认控制台存储更适合本地调试,正式环境应替换成可持久化的存储实现。
审计应该写在 Controller 还是 Service
更推荐写在 Service。
这样更容易围绕业务动作统一记录,也更方便长期维护。
UMetaOS 审计扩展
Aegis.UMeta.Audit 是当前组件的 UMetaOS 平台存储扩展。启用后,AuditLogger<T> 产生的审计记录会自动转发到 UMetaOS 的 OTel 审计通道,而不是输出到控制台。
如何引入
NuGet 包:Aegis.UMeta.Audit
在 Component.deps.json 中同时声明 Security.Audit 和 UMeta.Audit:
{
"Components": {
"Services": [
"Security.Audit",
"UMeta.Audit"
],
"Middlewares": [
"UMeta.Audit"
]
}
}
前置条件:需要先启用 UMeta.Logging 组件来初始化 OTel SDK,否则审计通道不可用。
使用方式
启用后,业务代码不需要任何改动。仍然注入 AuditLogger<T>,调用方式完全一致:
await _auditLogger
.SetOperator("1", OpEnum.医生, "张三")
.SetDomain(Domain.结算单)
.SetContent(OperationType.创建, "1", "创建结算单", "创建结算单xxxx")
.LogAsync();
区别仅在于底层存储从控制台变成了 UMetaOS 审计通道。UMetaAuditStorage 会自动将 AuditRecord 的字段映射到 OTel SDK 的审计格式,并附加当前请求的 TraceId 和 SpanId。