如何通过DbContext的SaveChanges实现AOP方案防止越权编辑?
解决方案
1. 定义归属实体接口
先创建统一接口,标记所有属于特定用户的实体,让权限验证逻辑能统一识别:
public interface IUserOwnedEntity { long UserId { get; } }
让WishList和WishListItem实现该接口(二者均关联到特定用户):
// WishList类实现接口 public class WishList : IUserOwnedEntity { public string Title { get; private set; } public string Description { get; private set; } public List<WishListItem> WishListItems { get; private set; } public User User { get; private set; } public long UserId { get; private set; } } // WishListItem类实现接口 public class WishListItem : IUserOwnedEntity { public string Title { get; private set; } public decimal RoughPrice { get; private set; } public int Priority { get; private set; } public WishList WishList { get; private set; } public long WishListId { get; private set; } // 通过关联的WishList获取所属用户ID public long UserId => WishList?.UserId ?? throw new InvalidOperationException("WishListItem必须关联有效的WishList"); }
2. 重写DbContext的SaveChanges方法
在WishListManagementDbContext中重写保存方法,嵌入权限验证逻辑,实现持久化层的统一拦截:
public class WishListManagementDbContext : DbContext { public DbSet<User> Users { get; set; } public DbSet<WishList> WishLists { get; set; } public DbSet<WishListItem> WishListItems { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.AddFromAssembly(typeof(WishListManagementDbContext).Assembly); base.OnModelCreating(modelBuilder); } public override int SaveChanges() { ValidateUserOwnership(); return base.SaveChanges(); } public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) { ValidateUserOwnership(); return base.SaveChangesAsync(cancellationToken); } private void ValidateUserOwnership() { // 获取当前登录用户名 var currentUsername = HttpContext.Current?.User?.Identity?.Name; if (string.IsNullOrEmpty(currentUsername)) { throw new UnauthorizedAccessException("用户未登录"); } // 查询当前用户ID var currentUser = Users.FirstOrDefault(u => u.Username == currentUsername); if (currentUser == null) { throw new InvalidOperationException("当前用户不存在于系统中"); } // 筛选出待新增/修改的用户归属实体 var userOwnedEntities = ChangeTracker.Entries<IUserOwnedEntity>() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified); foreach (var entry in userOwnedEntities) { var entity = entry.Entity; if (entity.UserId != currentUser.Id) { throw new UnauthorizedAccessException($"无权修改不属于你的实体:{entity.GetType().Name}"); } } } }
3. 补充User类主键属性
注意:你提供的User类缺少主键,需补充以满足EF映射要求:
public class User { public long Id { get; private set; } // 补充主键字段 public string Username { get; private set; } public string Password { get; private set; } public string Name { get; private set; } public DateTime? BirthDate { get; private set; } public List<WishList> WishLists { get; private set; } }
逻辑说明
- 该方案通过AOP思想在数据持久化层统一拦截验证,彻底避免在每个Action中重复编写权限判断代码。
- 所有关联用户的实体必须实现
IUserOwnedEntity接口,确保验证逻辑能统一识别处理。 - 验证逻辑会检查所有待新增/修改的实体,只有归属当前登录用户的实体才能被保存;若不符合权限,会抛出
UnauthorizedAccessException,你可通过全局异常过滤器捕获并返回对应错误响应。
内容的提问来源于stack exchange,提问作者Alireza Ahmadi
相关产品推荐
相关产品推荐

