You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 13:25:05