ABP - 审计日志 [AuditedAttribute、IAuditingManager、EntityAuditingHelper]

审计日志

  • 核心辅助类
    • AuditedAttribute:标记类/方法记录审计日志。
    • IAuditingManager:手动管理审计日志。
    • EntityAuditingHelper:实体审计辅助(自动填充创建/修改时间)。

审计日志(Auditing)核心类示例与讲解

ABP的审计日志功能用于自动记录系统中关键操作的细节(如“谁在什么时间操作了什么数据”),核心价值是追溯操作源头、保障数据安全。以下针对AuditedAttributeIAuditingManagerEntityAuditingHelper三个核心类,结合实际场景讲解用法:

一、AuditedAttribute:自动记录审计日志(声明式用法)

AuditedAttribute是最常用的审计工具,通过标记类或方法,让框架自动记录操作日志,无需手动编写日志代码。支持类级别(所有方法生效)和方法级别(单个方法生效)。

1. 类级别标记(所有方法自动审计)

using Volo.Abp.Auditing;
using Volo.Abp.Application.Services;

// 标记整个服务类,所有公共方法都会记录审计日志
[Audited]
public class BookAppService : ApplicationService
{
    private readonly IRepository<Book, Guid> _bookRepo;

    public BookAppService(IRepository<Book, Guid> bookRepo)
    {
        _bookRepo = bookRepo;
    }

    // 自动记录:谁(CreatorId)、何时(Time)、调用了此方法、输入参数(id)、返回结果(BookDto)
    public async Task<BookDto> GetAsync(Guid id)
    {
        var book = await _bookRepo.GetAsync(id);
        return ObjectMapper.Map<Book, BookDto>(book);
    }

    // 自动记录:新增操作的输入(CreateBookInput)、新增的书籍ID等
    public async Task<BookDto> CreateAsync(CreateBookInput input)
    {
        var book = ObjectMapper.Map<CreateBookInput, Book>(input);
        await _bookRepo.InsertAsync(book);
        return ObjectMapper.Map<Book, BookDto>(book);
    }
}

2. 方法级别标记(指定方法审计)

public class OrderAppService : ApplicationService
{
    // 仅标记此方法,其他方法不审计(适合只需要追溯关键操作的场景)
    [Audited]
    public async Task<OrderDto> PayAsync(Guid orderId, decimal amount)
    {
        // 支付业务逻辑...
    }

    // 不标记,不记录审计日志(如普通查询操作)
    public async Task<List<OrderDto>> GetUserOrdersAsync(Guid userId)
    {
        // 普通查询逻辑...
    }
}

讲解

  • 自动记录内容:框架会自动捕获「操作人ID、操作时间、方法名、输入参数、返回结果、执行时长」,无需手动传参。

  • 排除敏感参数:若方法参数包含密码等敏感信息,可通过[DisableAuditing]标记参数排除审计:

    public async Task ResetPasswordAsync(
        Guid userId, 
        [DisableAuditing] string newPassword // 不记录密码到审计日志
    )
    {
        // 重置密码逻辑...
    }
    
  • 适用场景:大多数常规CRUD操作、支付/审批等关键业务操作,用[Audited]可零代码实现审计追溯。

二、IAuditingManager:手动管理审计日志(编程式用法)

当需要自定义审计内容(如补充业务备注、记录非方法调用的操作)时,可通过IAuditingManager手动创建和提交审计日志,灵活度更高。

示例:手动记录自定义审计日志

using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;

public class InventoryService : ITransientDependency
{
    private readonly IAuditingManager _auditingManager;
    private readonly IRepository<Inventory, Guid> _inventoryRepo;

    // 注入IAuditingManager
    public InventoryService(IAuditingManager auditingManager, IRepository<Inventory, Guid> inventoryRepo)
    {
        _auditingManager = auditingManager;
        _inventoryRepo = inventoryRepo;
    }

    public async Task AdjustStockAsync(Guid productId, int adjustCount, string reason)
    {
        // 1. 开启审计日志(创建审计范围)
        using (var auditingScope = _auditingManager.BeginScope())
        {
            try
            {
                // 2. 业务逻辑:调整库存
                var inventory = await _inventoryRepo.GetAsync(x => x.ProductId == productId);
                inventory.Stock += adjustCount;
                await _inventoryRepo.UpdateAsync(inventory);

                // 3. 自定义审计内容(补充业务信息)
                var auditLog = auditingScope.Log;
                auditLog.MethodName = "AdjustStockAsync"; // 手动指定方法名
                auditLog.Comments = $"库存调整原因:{reason}"; // 补充业务备注
                auditLog.ExtraProperties["ProductId"] = productId; // 额外记录产品ID
                auditLog.ExtraProperties["BeforeStock"] = inventory.Stock - adjustCount; // 调整前库存
                auditLog.ExtraProperties["AfterStock"] = inventory.Stock; // 调整后库存

                // 4. 提交审计日志(若不调用,框架会在作用域结束时自动提交)
                await auditingManager.SaveAsync(auditLog);
            }
            catch (Exception ex)
            {
                // 5. 异常时标记日志状态(可选)
                auditingScope.Log.Exception = ex;
                throw;
            }
        }
    }
}

讲解

  • 核心方法
    • BeginScope():创建审计范围,所有在范围内的操作可被记录到同一条日志。
    • SaveAsync(auditLog):手动提交日志(若不调用,框架会在using作用域结束时自动提交)。
  • 自定义字段:通过auditLog.ExtraProperties可添加任意业务相关的额外信息(如库存调整前后的值、操作原因),满足个性化追溯需求。
  • 适用场景:非标准方法调用的操作(如定时任务调整数据、批量处理)、需要补充业务备注的关键操作。

三、EntityAuditingHelper:实体审计辅助(自动填充审计字段)

EntityAuditingHelper用于手动触发实体的审计字段填充(如CreationTimeCreatorIdLastModificationTime),通常在“框架自动填充失效”的场景下使用(如手动new的实体、非仓储操作的实体)。

示例:手动填充实体审计字段

using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.DependencyInjection;

public class CustomDataService : ITransientDependency
{
    private readonly EntityAuditingHelper _auditingHelper;
    private readonly ICurrentUser _currentUser;
    private readonly MyCustomDbContext _dbContext; // 自定义数据库上下文

    public CustomDataService(
        EntityAuditingHelper auditingHelper, 
        ICurrentUser currentUser, 
        MyCustomDbContext dbContext)
    {
        _auditingHelper = auditingHelper;
        _currentUser = currentUser;
        _dbContext = dbContext;
    }

    public async Task AddCustomDataAsync(string dataContent)
    {
        // 1. 手动创建实体(未通过仓储,框架无法自动填充审计字段)
        var customData = new CustomData
        {
            Content = dataContent
            // 此时:CreationTime、CreatorId 等审计字段为默认值(如DateTime.MinValue、null)
        };

        // 2. 手动调用EntityAuditingHelper填充审计字段
        _auditingHelper.SetCreationAuditProperties(
            entity: customData,
            currentUserId: _currentUser.Id // 传递当前用户ID(用于填充CreatorId)
        );

        // 3. 手动将实体添加到数据库(非仓储操作)
        _dbContext.CustomDatas.Add(customData);
        await _dbContext.SaveChangesAsync();

        // 最终实体的审计字段:
        // CreationTime = 当前时间
        // CreatorId = 当前用户ID(如Guid.Parse("..."))
    }
}

// 定义带审计字段的实体
public class CustomData : Entity<Guid>, IHasCreationTime, IHasCreator
{
    public string Content { get; set; }
    public DateTime CreationTime { get; set; } // 需手动填充的审计字段
    public Guid? CreatorId { get; set; }       // 需手动填充的审计字段
}

讲解

  • 核心方法
    • SetCreationAuditProperties(entity, currentUserId):填充“创建审计”字段(CreationTimeCreatorId)。
    • SetModificationAuditProperties(entity, currentUserId):填充“修改审计”字段(LastModificationTimeLastModifierId)。
    • SetDeletionAuditProperties(entity, currentUserId):填充“删除审计”字段(DeletionTimeDeleterId,适用于软删除)。
  • 适用场景:未通过ABP仓储操作的实体(如直接用EF Core的DbContext)、手动new的实体,需要补充审计字段时使用。
  • 自动填充场景:若实体通过IRepositoryInsertAsync/UpdateAsync操作,框架会自动调用EntityAuditingHelper,无需手动处理(如之前的BookAppService示例)。

四、审计日志的核心配置与查看

1. 基础配置(模块中启用审计)

ABP默认启用审计,若需调整配置(如排除某些方法、设置日志保存方式),可在模块的ConfigureServices中配置:

public override void ConfigureServices(ServiceConfigurationContext context)
{
    Configure<AbpAuditingOptions>(options =>
    {
        // 全局排除审计的方法(如所有以"Internal"开头的方法)
        options.IgnoredMethods.Add("Internal*");
        
        // 审计日志保存到数据库(默认),也可配置保存到文件/第三方日志系统
        options.IsEnabledForGetRequests = true; // 是否记录GET请求(默认false,避免查询日志过多)
    });
}

2. 查看审计日志

审计日志默认保存到AbpAuditLogs表(EF Core),可通过IAuditLogRepository查询历史日志:

public async Task<List<AuditLogDto>> GetAuditLogsAsync(DateTime startDate)
{
    var logs = await _auditLogRepo.GetListAsync(
        filter: log => log.ExecutionTime >= startDate,
        orderBy: log => log.ExecutionTime.Descending()
    );
    return ObjectMapper.Map<List<AuditLog>, List<AuditLogDto>>(logs);
}

五、总结:三类核心类的适用场景

类/特性 用法方式 核心优势 适用场景
AuditedAttribute 声明式(特性) 零代码、自动记录 常规CRUD、关键方法(如支付、审批)
IAuditingManager 编程式(代码) 灵活自定义、补充业务信息 非标准操作、需额外备注的关键业务
EntityAuditingHelper 编程式(代码) 手动填充审计字段 非仓储操作的实体、手动创建的实体

通过这三类工具,ABP可覆盖从“自动记录”到“手动定制”的全场景审计需求,确保系统操作可追溯、数据安全有保障。

posted @ 2025-10-24 21:10  【唐】三三  阅读(3)  评论(0)    收藏  举报