审计日志
维基百科:"审计追踪(又称审计日志)是按时间顺序记录的安全相关活动序列,提供操作、流程或事件受影响过程的书面证据"。
ABP提供了一套可扩展的审计日志系统,通过约定实现自动化日志记录,并提供配置点来控制审计日志的详细程度。
每个Web请求通常会创建并保存一个审计日志对象(详见下文审计日志对象章节),包含:
- 请求与响应详情(如URL、HTTP方法、浏览器信息、HTTP状态码等)
- 执行的操作(控制器动作及应用服务方法调用及其参数)
- Web请求中发生的实体变更
- 异常信息(请求执行过程中出现错误时)
- 请求耗时(用于衡量应用性能)
启动模板已预配置适用于大多数应用的审计日志系统。本文档将帮助您实现更精细的审计日志控制。
数据库提供程序支持
- Entity Framework Core提供程序完整支持
- MongoDB提供程序不支持实体变更记录,其他功能正常
UseAuditing()
需在ASP.NET Core请求管道中添加UseAuditing()中间件以创建和保存审计日志。若使用启动模板创建应用,此配置已自动完成。
AbpAuditingOptions
AbpAuditingOptions是配置审计日志系统的核心选项对象。可在模块的ConfigureServices方法中配置:
Configure<AbpAuditingOptions>(options =>
{
options.IsEnabled = false; //禁用审计系统
});
可配置选项包括:
IsEnabled(默认:true):总开关,启用/禁用审计系统。设为false时其他选项无效HideErrors(默认:true):保存审计日志对象时若发生错误,系统将隐藏错误并写入常规日志。若审计日志对系统至关重要,可设为false以抛出异常IsEnabledForAnonymousUsers(默认:true):若仅需为认证用户记录日志,可设为false。匿名用户的UserId将显示为nullAlwaysLogOnException(默认:true):设为true时,发生异常/错误将始终保存审计日志(不受其他选项限制,但受IsEnabled控制)IsEnabledForIntegrationService(默认:false):默认禁用集成服务的审计日志,设为true启用IsEnabledForGetRequests(默认:false):HTTP GET请求通常不引发数据库变更,系统默认不记录。设为true可启用GET请求日志DisableLogActionInfo(默认:false):设为true时将停止记录AuditLogActionInfoApplicationName:多应用共享审计日志数据库时,可设置此属性以区分不同应用日志。未设置时默认使用IApplicationInfoAccessor.ApplicationName值(即入口程序集名称)IgnoredTypes:审计日志忽略的Type列表。若为实体类型,则该类实体的变更不会被记录。此列表也用于动作参数序列化EntityHistorySelectors:用于确定是否记录实体变更的选择器列表,详见下文SaveEntityHistoryWhenNavigationChanges(默认:true):设为true时,导航属性变更将保存实体变更记录Contributors:AuditLogContributor实现列表,用于扩展审计日志系统,详见"审计日志贡献者"章节AlwaysLogSelectors:匹配条件时始终保存审计日志的选择器列表
实体历史选择器
记录所有实体的全部变更将占用大量数据库空间,因此除非显式配置,审计日志系统默认不保存任何实体变更。
若要记录所有实体变更,可使用AddAllEntities()扩展方法:
Configure<AbpAuditingOptions>(options =>
{
options.EntityHistorySelectors.AddAllEntities();
});
options.EntityHistorySelectors实际上是类型谓词列表,可通过lambda表达式定义筛选条件。
下例选择器实现了与上述AddAllEntities()相同的效果:
Configure<AbpAuditingOptions>(options =>
{
options.EntityHistorySelectors.Add(
new NamedTypeSelector(
"MySelectorName",
type =>
{
if (typeof(IEntity).IsAssignableFrom(type))
{
return true;
}
else
{
return false;
}
}
)
);
});
条件typeof(IEntity).IsAssignableFrom(type)对所有实现IEntity接口的类返回true(即应用中所有实体)。可根据需求条件化返回true或false。
options.EntityHistorySelectors提供了灵活的实体选择机制。另一种方式是在每个实体上使用Audited和DisableAuditing特性。
AbpAspNetCoreAuditingOptions
AbpAspNetCoreAuditingOptions用于配置ASP.NET Core层的审计日志选项。可在模块的ConfigureServices方法中配置:
Configure<AbpAspNetCoreAuditingOptions>(options =>
{
options.IgnoredUrls.Add("/products");
});
IgnoredUrls是唯一选项,用于设置忽略的URL前缀列表。上例中所有以/products开头的URL将不记录审计日志。
AbpAspNetCoreAuditingUrlOptions
AbpAspNetCoreAuditingUrlOptions用于配置ASP.NET Core层的审计日志选项。可在模块的ConfigureServices方法中配置:
Configure<AbpAspNetCoreAuditingUrlOptions>(options =>
{
options.IncludeQuery = true;
});
可配置选项包括:
IncludeSchema(默认:false):设为true时URL包含架构信息IncludeHost(默认:false):设为true时URL包含主机信息IncludeQuery(默认:false):设为true时URL包含查询字符串
服务级审计日志启用/禁用
控制器与动作的启用/禁用
默认记录所有控制器动作(GET请求受IsEnabledForGetRequests控制)。
可使用[DisableAuditing]禁用特定控制器:
[DisableAuditing]
public class HomeController : AbpController
{
//...
}
在动作级别使用[DisableAuditing]进行控制:
public class HomeController : AbpController
{
[DisableAuditing]
public async Task<ActionResult> Home()
{
//...
}
public async Task<ActionResult> OtherActionLogged()
{
//...
}
}
应用服务与方法的启用/禁用
默认记录应用服务方法调用。可在服务或方法级别使用[DisableAuditing]。
其他服务的启用/禁用
动作审计日志可应用于任何类(通过依赖注入注册并解析),但默认仅启用控制器和应用服务。
需审计日志的类或方法可使用[Audited]和[DisableAuditing]。此外,类可通过(直接或间接)实现IAuditingEnabled接口默认启用审计日志。
实体与属性的启用/禁用
以下情况忽略实体变更审计日志:
- 实体类型已加入
AbpAuditingOptions.IgnoredTypes列表(如前所述) - 对象非实体(未直接或间接实现
IEntity接口——所有实体默认实现此接口) - 实体类型非公开
其他情况下,可使用Audited启用实体变更审计日志:
[Audited]
public class MyEntity : Entity<Guid>
{
//...
}
或禁用实体审计日志:
[DisableAuditing]
public class MyEntity : Entity<Guid>
{
//...
}
仅当实体被AbpAuditingOptions.EntityHistorySelectors选中时才需禁用审计日志。
可通过禁用特定属性实现精细控制:
[Audited]
public class MyUser : Entity<Guid>
{
public string Name { get; set; }
public string Email { get; set; }
[DisableAuditing] //审计日志忽略密码字段
public string Password { get; set; }
}
系统将记录MyUser实体变更,但忽略可能危及安全的Password属性。
有时可能需仅记录少量属性而忽略其他。为避免为每个属性添加[DisableAuditing],可在实体标记[DisableAuditing]后仅为所需属性添加[Audited]:
[DisableAuditing]
public class MyUser : Entity<Guid>
{
[Audited] //仅记录Name变更
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
忽略更新审计属性与发布实体更新事件
[DisableAuditing]特性在应用于实体属性时支持额外配置选项:
- UpdateModificationProps(默认:
true):设为false时,该属性变更不更新审计属性(如LastModificationTime) - PublishEntityEvent(默认:
true):设为false时,该属性变更不发布实体变更事件(EntityUpdatedEvent)
public class MyUser : Entity<Guid>
{
public string Name { get; set; }
[DisableAuditing(UpdateModificationProps = false, PublishEntityEvent = false)]
public string ReadCount { get; set; }
}
此例将在 ReadCount 属性更新时忽略更新审计属性并禁止发布实体更新事件。
UpdateModificationProps和PublishEntityEvent仅适用于 Entity Framework Core ,不适用于 MongoDB 。
IAuditingStore
IAuditingStore 接口用于保存审计日志对象(详见下文)。若需将审计日志保存至自定义数据存储,可实现此接口并通过 依赖注入系统 替换。
未注册审计存储时使用 SimpleLogAuditingStore,仅将审计对象写入标准 日志系统 。
启动模板 中预配置的 审计日志模块 将审计日志对象保存至数据库(支持多数据库提供程序)。多数情况下无需关注 IAuditingStore 的实现细节。
审计日志对象
默认每个Web请求创建一个审计日志对象,其结构关系如下图所示:
- AuditLogInfo:根对象,包含以下属性:
ApplicationName:多应用共享数据库时区分应用日志UserId:当前用户ID(已登录时)UserName:当前用户名(已登录时,此值用于避免依赖身份模块查询)TenantId:多租户应用中的当前租户IDTenantName:多租户应用中的当前租户名ExecutionTime:审计日志对象创建时间ExecutionDuration:请求总执行时长(毫秒),用于性能监控ClientId:当前客户端ID(已认证时)。客户端通常为通过HTTP API使用系统的第三方应用ClientName:当前客户端名(可用时)ClientIpAddress:客户端/用户设备IP地址CorrelationId:当前关联ID,用于关联不同应用(或微服务)在单一逻辑操作中写入的审计日志BrowserInfo:当前用户浏览器信息(可用时)HttpMethod:当前请求的HTTP方法(GET、POST、PUT、DELETE等)HttpStatusCode:请求的HTTP响应状态码Url:请求URL
- AuditLogActionInfo:审计日志动作通常是Web请求期间的控制器动作或应用服务方法调用。一个审计日志可包含多个动作,动作对象包含:
ServiceName:执行的控制器/服务名MethodName:执行的控制器/服务方法名Parameters:方法参数的JSON格式文本ExecutionTime:方法执行时间ExecutionDuration:方法执行时长(毫秒),用于性能监控
- EntityChangeInfo:表示Web请求中的实体变更。审计日志可包含零或多个实体变更,变更信息包含:
ChangeTime:实体变更时间ChangeType:枚举值:Created(0)、Updated(1)、Deleted(2)EntityId:变更实体的IDEntityTenantId:实体所属租户IDEntityTypeFullName:实体类型全名(如Book实体为Acme.BookStore.Book)
- EntityPropertyChangeInfo:表示实体属性的变更。实体变更信息可包含一或多个属性变更,包含:
NewValue:属性新值。实体删除时为nullOriginalValue:变更前的旧值/原始值。实体新建时为nullPropertyName:实体类的属性名PropertyTypeFullName:属性类型全名
- Exception:审计日志对象可包含零或多个异常,用于记录失败请求的详细信息
- Comment:可向审计日志条目添加自定义消息的任意字符串值,审计日志对象可包含零或多个注释
除上述标准属性外,AuditLogInfo、AuditLogActionInfo和EntityChangeInfo对象实现了IHasExtraProperties接口,可添加自定义属性。
审计日志贡献者
可通过继承AuditLogContributor类扩展审计系统,该类定义了PreContribute和PostContribute方法。
唯一预构建的贡献者是AspNetCoreAuditLogContributor类,用于设置HTTP请求的相关属性。
贡献者可设置AuditLogInfo类的属性和集合以添加更多信息。
示例:
public class MyAuditLogContributor : AuditLogContributor
{
public override void PreContribute(AuditLogContributionContext context)
{
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
context.AuditInfo.SetProperty(
"MyCustomClaimValue",
currentUser.FindClaimValue("MyCustomClaim")
);
}
public override void PostContribute(AuditLogContributionContext context)
{
context.AuditInfo.Comments.Add("Some comment...");
}
}
context.ServiceProvider可用于从依赖注入解析服务context.AuditInfo可用于操作当前审计日志对象
创建贡献者后,需将其加入AbpAuditingOptions.Contributors列表:
Configure<AbpAuditingOptions>(options =>
{
options.Contributors.Add(new MyAuditLogContributor());
});
IAuditLogScope & IAuditingManager
本节介绍高级用法中的IAuditLogScope和IAuditingManager服务。
审计日志范围是一个环境范围,用于构建和保存审计日志对象(如前所述)。默认情况下,审计日志中间件(见上文UseAuditing()章节)为Web请求创建审计日志范围。
访问当前审计日志范围
前文所述的审计日志贡献者是操作审计日志对象的全局方式,适用于从服务获取值。
若需在应用任意点操作审计日志对象,可访问当前审计日志范围并获取当前审计日志对象(独立于范围管理方式)。示例:
public class MyService : ITransientDependency
{
private readonly IAuditingManager _auditingManager;
public MyService(IAuditingManager auditingManager)
{
_auditingManager = auditingManager;
}
public async Task DoItAsync()
{
var currentAuditLogScope = _auditingManager.Current;
if (currentAuditLogScope != null)
{
currentAuditLogScope.Log.Comments.Add(
"Executed the MyService.DoItAsync method :)"
);
currentAuditLogScope.Log.SetProperty("MyCustomProperty", 42);
}
}
}
需始终检查_auditingManager.Current是否为null,因其由外部范围控制,无法预知调用方法前是否已创建审计日志范围。
手动创建审计日志范围
极少需要手动创建审计日志范围,但必要时可使用IAuditingManager创建:
public class MyService : ITransientDependency
{
private readonly IAuditingManager _auditingManager;
public MyService(IAuditingManager auditingManager)
{
_auditingManager = auditingManager;
}
public async Task DoItAsync()
{
using (var auditingScope = _auditingManager.BeginScope())
{
try
{
//调用其他服务...
}
catch (Exception ex)
{
//添加异常
_auditingManager.Current.Log.Exceptions.Add(ex);
throw;
}
finally
{
//始终保存日志
await auditingScope.SaveAsync();
}
}
}
}
可调用其他服务(可能引发进一步调用或实体变更),所有这些交互将在finally块中作为单一审计日志对象保存。
审计日志模块
审计日志模块基本实现了IAuditingStore接口,将审计日志对象保存至数据库,支持多数据库提供程序。此模块默认包含在启动模板中。
详见审计日志模块文档。
抠丁客



