数据访问控制是任何企业级应用中不可或缺的一部分,特别是在涉及敏感信息或需要按照用户角色限制数据访问的情况下。Entity Framework Core(EF Core)通过引入查询过滤器(Query Filters),为开发者提供了一种优雅的方式来实现细粒度的数据访问控制。本文将以杂文的形式,详细探讨如何在 EF Core 中使用查询过滤器来实现细粒度的数据访问控制,并通过具体的代码示例展示其实现过程。
首先,我们需要创建一个基于 EF Core 的项目。打开 Visual Studio,创建一个新的 .NET Core Web API 项目,并选择模板创建项目。接着,添加 EF Core 相关的 NuGet 包,如 Microsoft.EntityFrameworkCore.SqlServer
和 Microsoft.EntityFrameworkCore.Tools
。
配置数据库上下文
在 Models
文件夹中,创建一个 ApplicationDbContext
类,用于定义数据库上下文。
using Microsoft.EntityFrameworkCore;
namespace YourProjectName.Models
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<User> Users {
get; set; }
public DbSet<UserProfile> UserProfiles {
get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 配置查询过滤器
modelBuilder.Entity<User>()
.HasQueryFilter(u => u.IsActive); // 默认只查询激活状态的用户
modelBuilder.Entity<UserProfile>()
.HasQueryFilter(up => up.IsVisible); // 默认只查询可见的用户资料
}
}
}
定义领域模型
在 Models
文件夹中,定义两个实体类 User
和 UserProfile
。
namespace YourProjectName.Models
{
public class User
{
public int UserId {
get; set; }
public string UserName {
get; set; }
public bool IsActive {
get; set; } // 是否激活
}
public class UserProfile
{
public int UserProfileId {
get; set; }
public int UserId {
get; set; }
public User User {
get; set; }
public string Bio {
get; set; }
public bool IsVisible {
get; set; } // 是否可见
}
}
使用查询过滤器
查询过滤器是一种全局过滤规则,可以在不显式指定条件的情况下应用于所有查询。在上面的配置中,我们为 User
和 UserProfile
实体分别设置了默认的查询过滤器。这意味着,当我们在代码中查询这些实体时,默认只会返回满足过滤条件的数据。
下面是一个简单的 API 控制器,演示如何使用查询过滤器来查询数据。
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using YourProjectName.Models;
namespace YourProjectName.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly ApplicationDbContext _context;
public UsersController(ApplicationDbContext context)
{
_context = context;
}
// GET api/users
[HttpGet]
public ActionResult<IEnumerable<User>> GetUsers()
{
return _context.Users.ToList();
}
// GET api/users/with-profiles
[HttpGet("with-profiles")]
public ActionResult<IEnumerable<User>> GetUsersWithProfiles()
{
return _context.Users
.Include(u => u.UserProfile)
.ToList();
}
}
}
关闭查询过滤器
有时候,我们可能需要关闭全局查询过滤器以便执行不受限制的查询。EF Core 允许我们在查询时显式地关闭过滤器。
// 在 UsersController 中添加以下方法
[HttpGet("all-users")]
public ActionResult<IEnumerable<User>> GetAllUsers()
{
return _context.Users.IgnoreQueryFilters().ToList();
}
[HttpGet("all-user-profiles")]
public ActionResult<IEnumerable<UserProfile>> GetAllUserProfiles()
{
return _context.UserProfiles.IgnoreQueryFilters().ToList();
}
细粒度的数据访问控制
查询过滤器不仅可以用于全局过滤,还可以根据不同的条件应用不同的过滤规则。例如,可以根据用户的登录状态或角色来决定是否应用过滤器。
// 在 Startup.cs 中添加用户身份验证和授权相关的代码
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});
services.AddControllers();
}
应用角色级别的过滤器
假设我们有一个管理员角色,他们可以查看所有用户的数据,而普通用户只能看到自己的数据。我们可以通过在控制器中应用策略来实现这一点。
using Microsoft.AspNetCore.Authorization;
// 在 UsersController 中添加以下方法
[HttpGet("all-users-with-policy")]
[Authorize(Policy = "AdminOnly")]
public ActionResult<IEnumerable<User>> GetAllUsersWithPolicy()
{
return _context.Users.ToList();
}
[HttpGet("my-profile")]
[Authorize]
public ActionResult<UserProfile> GetMyProfile()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var userProfile = _context.UserProfiles
.FirstOrDefault(up => up.UserId == int.Parse(userId));
if (userProfile == null)
{
return NotFound();
}
return userProfile;
}
总结
通过上述步骤,我们展示了如何在 Entity Framework Core 中使用查询过滤器来实现细粒度的数据访问控制。从配置数据库上下文到定义领域模型,再到实现和使用查询过滤器,每个环节都体现了如何利用 EF Core 的强大功能来处理复杂的数据访问需求。希望本文提供的示例代码和技术指南能够帮助你在实际项目中更好地应用这些技术,构建出安全且高效的数据库访问层。
查询过滤器不仅能够简化数据访问控制的实现,还能提高应用程序的安全性和用户体验。结合 EF Core 的强大功能,我们可以构建出高度灵活且易于扩展的数据访问层,从而提高生产力并降低维护成本。通过细粒度的数据访问控制,我们可以确保只有经过适当授权的用户才能访问他们被允许查看的数据,这对于保护敏感信息至关重要。