ASP.NET Core中基于用户的访问控制方案求助
适配方案推荐:EF Core全局查询筛选器 + SQL Server行级安全(RLS)
你的场景属于基于资源所有者的细粒度访问控制,RBAC/PBAC更适合粗粒度的角色/权限划分,无法直接满足“仅允许资源所有者修改自己数据”的需求,而以下两个微软官方方案的组合完全适配你的要求,且均经过严格测试、能获得持续安全更新:
一、EF Core 全局查询筛选器(自动拦截应用层操作)
这是EF Core原生特性,能自动为所有查询、更新、删除操作添加筛选条件,确保用户仅能访问/修改自己创建的书籍,无需在每个Action中手动编写检查逻辑。
实现步骤:
- 扩展Book实体:添加
CreatedByUserId字段,用于存储书籍创建者的用户ID:
public class Book { public int BookId { get; set; } public string Title { get; set; } public int CreatedByUserId { get; set; } // 新增字段 }
- 在DbContext中配置全局筛选器:
通过注入IHttpContextAccessor获取当前登录用户ID,为Book实体添加全局筛选规则:
public class AppDbContext : DbContext { private readonly IHttpContextAccessor _httpContextAccessor; public AppDbContext(DbContextOptions<AppDbContext> options, IHttpContextAccessor httpContextAccessor) : base(options) { _httpContextAccessor = httpContextAccessor; } public DbSet<Book> Books { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 获取当前登录用户ID var userIdClaim = _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier); if (int.TryParse(userIdClaim, out var currentUserId)) { // 为Book实体添加全局查询筛选器 modelBuilder.Entity<Book>() .HasQueryFilter(book => book.CreatedByUserId == currentUserId); } } }
- 自动应用筛选逻辑:
配置完成后,EF Core会自动将筛选条件应用到所有针对Book的查询、Update、Delete操作。例如恶意用户B尝试修改用户A的书籍时,EF Core会自动附加CreatedByUserId = B的ID的条件,因查询不到匹配数据,更新操作会被静默拦截(或返回0行受影响)。
二、SQL Server 行级安全(RLS):数据库层面的兜底防护
作为应用层筛选的补充,RLS能在数据库层面拦截绕过EF Core的直接SQL操作(如恶意用户直接执行UPDATE语句),进一步提升安全性。
实现步骤:
- 创建安全函数:用于获取当前会话的用户ID(需与应用层传递的用户ID一致):
CREATE FUNCTION dbo.GetCurrentUserId() RETURNS INT WITH SCHEMABINDING AS BEGIN RETURN CAST(SESSION_CONTEXT(N'UserId') AS INT); END; GO
- 启用行级安全并创建访问策略:
-- 为Books表启用行级安全 ALTER TABLE dbo.Books ENABLE ROW LEVEL SECURITY; GO -- 创建策略:仅允许用户访问自己创建的书籍 CREATE POLICY Books_UserAccessPolicy ON dbo.Books FOR ALL -- 对SELECT/INSERT/UPDATE/DELETE均生效 USING (CreatedByUserId = dbo.GetCurrentUserId()); GO
- 在EF Core中传递用户ID到数据库会话:
在DbContext的SaveChangesAsync方法中,将当前用户ID写入SQL Server的会话上下文:
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) { var userIdClaim = _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier); if (!string.IsNullOrEmpty(userIdClaim)) { // 将用户ID写入SQL Server会话上下文 await Database.ExecuteSqlRawAsync( $"EXEC sp_set_session_context @key=N'UserId', @value={userIdClaim}", cancellationToken); } return await base.SaveChangesAsync(cancellationToken); }
方案优势
- 无需手动检查:全局筛选器自动覆盖所有EF Core操作,避免重复代码和人为疏漏。
- 官方支持:两个特性均为微软官方产品的原生功能,与EF Core 7完全兼容,可获得持续的安全更新和技术支持。
- 多层防护:应用层+数据库层的双重拦截,全面覆盖可能的攻击路径。
内容的提问来源于stack exchange,提问作者Bluebaron
相关产品推荐
相关产品推荐

