首页 > 解决方案 > 使用 EFCore 拦截器实现行级安全

问题描述

我有一个多租户 PostgreSQL 数据库,它使用行级安全性来控制租户应该能够看到的内容。

我正在使用 EF Core 和 ASP.NET Core 来访问这个数据库。为了处理租户访问,我使用连接拦截器来运行适当的数据库命令。例如:

为此,我创建了一个连接拦截器。

public override async Task ConnectionOpenedAsync(
    DbConnection connection, 
    ConnectionEndEventData eventData, 
    CancellationToken cancellationToken = default)
{
    using var command = connection.CreateCommand();
    var commandText = string.Join("\r\n",
        "SET SESSION ROLE TO tenant;",
        $"SET SESSION tenant.userId TO '{this.userId}';");

    command.CommandText = commandText;
    await command.ExecuteNonQueryAsync(cancellationToken);
    await base.ConnectionOpenedAsync(connection, eventData, cancellationToken);
}

所有这些都可以正常工作,但我还需要在非租户上下文中访问数据库。换句话说,我不希望上面的拦截器总是运行。

我正在努力寻找最好的方法来做到这一点。我最初的尝试是使用接口来定义租户或“全局”接口

// (MyDbContext implements both IMyTenantDbContext and IMyGlobalDbContext)
services.AddDbContext<IMyTenantDbContext, MyDbContext>((sp, options) =>
{
    var tenantInterceptor = sp.GetRequiredService<SetTenantConnectionInterceptor>();
    options
        .UseNpgsql(connectionString)
        .AddInterceptors(tenantInterceptor);
});
services.AddDbContext<IMyGlobalDbContext, MyDbContext>(options =>
{
     options
         .UseNpgsql(connectionString);
});

但是由于某种原因,DI 系统使用第一个 AddDbContext,即使我尝试注入一个IMyGlobalDbContext含义,拦截器仍在使用中。

我确信必须有更好的方法来做到这一点,所以如果我做的完全错误,请告诉我。

标签: c#entity-framework-coreef-core-3.1

解决方案


我建议将其设置为 userId 可以为 null 或“系统” userId,这意味着不运行拦截器代码,因此同一个拦截器可以处理这两种情况。


推荐阅读