EF Core 6如何禁止查询特定Always Encrypted加密列?
EF Core 6 拦截查询并检测加密列抛出异常的实现方案
可以通过EF Core的查询拦截器实现需求,在查询发送到数据库前检测是否包含加密列,直接抛出异常阻止执行。以下是具体实现步骤:
1. 标记加密列(两种方式选其一)
方式一:自定义特性标记
创建特性类标记实体中的加密属性:
[AttributeUsage(AttributeTargets.Property)] public class EncryptedColumnAttribute : Attribute { }
在实体类的加密属性上添加标记:
public class User { public int Id { get; set; } public string Name { get; set; } [EncryptedColumn] public string SSN { get; set; } // Always Encrypted加密列 }
方式二:通过EF模型注解标记
在OnModelCreating中配置加密列时添加自定义注解:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置Always Encrypted列 modelBuilder.Entity<User>() .Property(u => u.SSN) .HasColumnType("nvarchar(11) ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = CEK_Auto1, ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256')") .AddAnnotation("IsEncrypted", true); // 添加加密标记注解 }
2. 实现查询表达式拦截器
继承IQueryExpressionInterceptor,通过遍历查询表达式树检测是否引用加密列:
public class EncryptedColumnQueryInterceptor : IQueryExpressionInterceptor { private readonly IModel _model; public EncryptedColumnQueryInterceptor(IModel model) { _model = model; } public Expression QueryCompilationStarting(Expression queryExpression, QueryExpressionEventData eventData) { var visitor = new EncryptedColumnChecker(_model); visitor.Visit(queryExpression); if (visitor.DetectedEncryptedColumn) { throw new InvalidOperationException("查询包含加密列,禁止执行此类操作。"); } return queryExpression; } private class EncryptedColumnChecker : ExpressionVisitor { private readonly IModel _model; public bool DetectedEncryptedColumn { get; private set; } public EncryptedColumnChecker(IModel model) { _model = model; } protected override Expression VisitMember(MemberExpression node) { if (node.Member is PropertyInfo property && node.Member.DeclaringType != null) { // 检查特性标记(对应方式一) if (Attribute.IsDefined(property, typeof(EncryptedColumnAttribute))) { DetectedEncryptedColumn = true; return base.VisitMember(node); } // 检查模型注解标记(对应方式二) var entityType = _model.FindEntityType(node.Member.DeclaringType); var propertyEntry = entityType?.FindProperty(property); if (propertyEntry?.FindAnnotation("IsEncrypted")?.Value is true) { DetectedEncryptedColumn = true; } } return base.VisitMember(node); } } }
3. 注册拦截器到DbContext
在OnConfiguring方法中添加拦截器:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseSqlServer("你的数据库连接字符串") .AddInterceptors(new EncryptedColumnQueryInterceptor(this.Model)); }
说明
- 该方案在查询编译阶段检测,比拦截SQL命令更可靠,避免了别名、同名列导致的误判。
- 一旦检测到查询引用加密列,会直接抛出
InvalidOperationException,阻止查询发送到数据库。
内容的提问来源于stack exchange,提问作者kal
相关产品推荐
相关产品推荐

