在 ASP.NET Core Web API 中实现审计跟踪

一.介绍

审计跟踪对于跟踪数据变化、维护安全性规至关重要。在本文中,我们将在 ASP.NET Core Web API 中实现审计跟踪。该示例将涵盖从设置项目到执行 CRUD 操作和验证审计日志的所有内容。

二.先决条件

  1. Visual Studio 或 Visual Studio Code
  2. SQL Server(或合适的 SQL 数据库)

三.实现方法

步骤 1.创建一个新项目

打开终端或命令提示符并运行以下命令来创建一个新的 ASP.NET Core Web API 项目:

复制代码
dotnet new webapi -n AuditTrailExample
cd AuditTrailImplementtionInAspNetCoreWebAPI

步骤 2.安装所需的 NuGet 包

安装 Entity Framework Core 和 SQL Server 必要的包。

复制代码
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.AspNetCore.Http.Abstractions

步骤 3. 定义数据模型

创建产品和审计日志实体。

3.1.产品实体

创建一个新的文件夹 Models 并添加 Product 类。

复制代码
namespace AuditTrailImplementtionInAspNetCoreWebAPI.Model
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public int Stock { get; set; }
    }

}
3.2.审计日志实体

添加 AuditLog 类。

复制代码
namespace AuditTrailImplementtionInAspNetCoreWebAPI.Model
{
    public class AuditLog
    {
        public int Id { get; set; }
        public string? UserId { get; set; }
        public DateTime Timestamp { get; set; }
        public string? Action { get; set; }
        public string? TableName { get; set; }
        public string? RecordId { get; set; }
        public string? Changes { get; set; }
    }
}

步骤 4.配置数据库上下文

创建新文件夹Data并添加ApplicationDbContext类:

复制代码
using AuditTrailImplementtionInAspNetCoreWebAPI.Model;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using System.Collections.Generic;
namespace AuditTrailImplementtionInAspNetCoreWebAPI.Data
{
    public class ApplicationDbContext : DbContext
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor httpContextAccessor)
            : base(options)
        {
            _httpContextAccessor = httpContextAccessor;
        }
        public DbSet<AuditLog> AuditLogs { get; set; }
        public DbSet<Product> Products { get; set; }
        public override int SaveChanges()
        {
            var auditEntries = OnBeforeSaveChanges();
            var result = base.SaveChanges();
            OnAfterSaveChanges(auditEntries);
            return result;
        }
        private List<AuditEntry> OnBeforeSaveChanges()
        {
            ChangeTracker.DetectChanges();
            var auditEntries = new List<AuditEntry>();
            var userId = _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
            foreach (var entry in ChangeTracker.Entries())
            {
                if (entry.Entity is AuditLog || entry.State == EntityState.Detached || entry.State == EntityState.Unchanged)
                {
                    continue;
                }
                var auditEntry = new AuditEntry(entry)
                {
                    TableName = entry.Entity.GetType().Name,
                    Action = entry.State.ToString(),
                    UserId = "1234"
                };
                auditEntries.Add(auditEntry);
                foreach (var property in entry.Properties)
                {
                    string propertyName = property.Metadata.Name;
                    if (property.IsTemporary)
                    {
                        auditEntry.TemporaryProperties.Add(property);
                        continue;
                    }
                    if (entry.State == EntityState.Added)
                    {
                        auditEntry.NewValues[propertyName] = property.CurrentValue;
                    }
                    else if (entry.State == EntityState.Deleted)
                    {
                        auditEntry.OldValues[propertyName] = property.OriginalValue;
                    }
                    else if (entry.State == EntityState.Modified && property.IsModified)
                    {
                        auditEntry.OldValues[propertyName] = property.OriginalValue;
                        auditEntry.NewValues[propertyName] = property.CurrentValue;
                    }
                }
            }
            foreach (var auditEntry in auditEntries.Where(e => !e.HasTemporaryProperties))
            {
                AuditLogs.Add(auditEntry.ToAuditLog());
            }
            return auditEntries.Where(e => e.HasTemporaryProperties).ToList();
        }
        private void OnAfterSaveChanges(List<AuditEntry> auditEntries)
        {
            if (auditEntries == null || auditEntries.Count == 0)
            {
                return;
            }
            foreach (var auditEntry in auditEntries)
            {
                foreach (var prop in auditEntry.TemporaryProperties)
                {
                    if (prop.Metadata.IsPrimaryKey())
                    {
                        auditEntry.KeyValues[prop.Metadata.Name] = prop.CurrentValue;
                    }
                    else
                    {
                        auditEntry.NewValues[prop.Metadata.Name] = prop.CurrentValue;
                    }
                }
                AuditLogs.Add(auditEntry.ToAuditLog());
            }
            SaveChanges();
        }
    }
}
4.1.添加连接字符串

在 appsettings.json 中,将连接字符串添加到您的 SQL Server:

复制代码
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=SARDAR-MUDASSAR\\SQLEXPRESS2022;database=AuditLog;User ID=sa;Password=Smak$95;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
  },
  "AllowedHosts": "*"
}

步骤 5.配置服务和中间件

更新 Program.cs 文件以配置服务和中间件:

复制代码
using AuditTrailImplementtionInAspNetCoreWebAPI.Data;
using Microsoft.EntityFrameworkCore;
namespace AuditTrailImplementtionInAspNetCoreWebAPI
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            // Add services to the container.
            builder.Services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
            builder.Services.AddControllers();
            builder.Services.AddHttpContextAccessor();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();
            var app = builder.Build();
            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }
            app.UseHttpsRedirection();
            app.UseAuthorization();
            app.MapControllers();
            app.Run();
        }
    }
}

步骤 6. 创建产品控制器

创建一个 Controllers 文件夹并添加 ProductsController 类。

复制代码
using AuditTrailImplementtionInAspNetCoreWebAPI.Data;
using AuditTrailImplementtionInAspNetCoreWebAPI.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;
namespace AuditTrailImplementtionInAspNetCoreWebAPI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class ProductsController : ControllerBase
    {
        private readonly ApplicationDbContext _context;
        public ProductsController(ApplicationDbContext context)
        {
            _context = context;
        }
        [HttpGet]
        public async Task<IActionResult> GetProducts()
        {
            var products = await _context.Products.ToListAsync();
            return Ok(products);
        }
        [HttpGet("{id}")]
        public async Task<IActionResult> GetProduct(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }
        [HttpPost]
        public async Task<IActionResult> CreateProduct([FromBody] Product newProduct)
        {
            _context.Products.Add(newProduct);
            await _context.SaveChangesAsync();
            return CreatedAtAction(nameof(GetProduct), new { id = newProduct.Id }, newProduct);
        }
        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateProduct(int id, [FromBody] Product updatedProduct)
        {
            var product = await _context.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }
            product.Name = updatedProduct.Name;
            product.Price = updatedProduct.Price;
            product.Stock = updatedProduct.Stock;
            await _context.SaveChangesAsync();
            return NoContent();
        }
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteProduct(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }
            _context.Products.Remove(product);
            await _context.SaveChangesAsync();
            return NoContent();
        }
    }
}

步骤 7. 应用迁移并更新数据库

生成迁移文件并更新数据库。

复制代码
add migration IMAuditTrail22042024
Update-database

步骤 8. 测试实施

使用 API 执行 CRUD 操作并验证 AuditLogs 表是否填充了适当的条目。

四.CRUD 操作示例

  1. 创建新产品

    curl -X POST "https://localhost:5001/products" -H "accept: text/plain" -H "Content-Type: application/json" -d "{ "name": "New Product", "price": 19.99, "stock": 100 }"

  2. 更新产品

    curl -X PUT "https://localhost:5001/products/1" -H "accept: text/plain" -H "Content-Type: application/json" -d "{ "name": "Updated Product", "price": 29.99, "stock": 150 }"

  3. 删除产品

    curl -X DELETE "https://localhost:5001/products/1" -H "accept: text/plain"

五.检查审计日志

执行上述操作后,检查数据库中的 AuditLogs 表,确保其中填充了预期的条目。

六.结论

通过遵循此端到端示例,您已成功在 ASP.NET Core Web API 中实现了审计跟踪。此实现有助于跟踪数据的变化,这对于维护安全性、合规性和调试问题至关重要。

相关推荐
print_out1 小时前
geoserver发布图层修复安全漏洞
中间件·安全威胁分析
吳所畏惧11 小时前
Linux环境/麒麟V10SP3下离线安装Redis、修改默认密码并设置Redis开机自启动
linux·运维·服务器·redis·中间件·架构·ssh
慧一居士14 小时前
xxl-job服务搭建,以及 springboot 集成xxl-job 项目完整步骤示例
分布式·中间件
odoo中国17 小时前
Pgpool-II 在 PostgreSQL 中的用例场景与优势
数据库·postgresql·中间件·pgpool
yuanmenghao1 天前
自动驾驶中间件iceoryx - 内存与 Chunk 管理(三)
数据结构·c++·算法·链表·中间件·自动驾驶
源代码•宸1 天前
goframe框架签到系统项目开发(补签逻辑实现、编写Lua脚本实现断签提醒功能、简历示例)
数据库·后端·中间件·go·lua·跨域·refreshtoken
忧郁蓝调262 天前
Redis单节点主从复制数据迁移
数据库·redis·缓存·云原生·中间件·云计算·paas
Code Slacker2 天前
西安交通大学软件学院——分布式系统练习题(选择题)
中间件·期末复习·分布式系统·练习题
拆房老料2 天前
BaseMetas Fileview 开源在线文件预览引擎首发:面向社区,免费赋能
中间件·开源·开源软件
transitory_truth3 天前
Apollo使用记录
java·中间件