我在数据库中的表中加了一些加密代码,我正在维护。这是大约2012年,所以没有"永远在"加密。 3列包含加密数据。
如果我对EF域进行反向工程,则为该表创建的模型包含那些类型为byte[]
的列的属性。这是预期的,因为列是varbinary
。所以,它看起来像这样:
class Person
{
public byte[] FirstName { get; set; } // FirstName
}
是否有一种优雅的方式来进行某种EF映射/配置,以便FirstName类的类型为string
并且它可以通过框架自动解密?我意识到我可以使用sql
实例化一个Person对象,但将这个处理卸载到框架会很不错。
我已经看到两种解决方案中的一种,人们基本上都在为每个属性使用sql查询。它们使用Encrypt属性修饰属性并迭代每个属性的属性。但是对于列表中的每个对象的每个属性都有一个sql查询 - 这不完全可以扩展。
有没有人"解决了#34;这个问题之前?
注意:要检索数据,首先需要发送类似于:
的sql语句OPEN SYMMETRIC KEY SomeKey DECRYPTION BY CERTIFICATE SomeCertificate
由于
答案 0 :(得分:2)
在这个答案中,我将列出在EF中处理加密列所需要做的事情。因此,相关列的类型为VARBINARY(MAX)
。让我们说你的表看起来像这样:
CREATE TABLE dbo.Person
(
SomeId int NOT NULL,
CreatedByUserId uniqueidentifier NULL,
CreatedUtcDate datetimeoffset(7) NULL,
Rowversion timestamp NULL,
FirstName varbinary(MAX) NULL,
LastName varbinary(MAX) NULL
)
第1步 - 创建一个返回解密列的视图。视图应基本上与您的表相同,但对于保存加密数据的列,它应返回解密的数据。它看起来像这样:
CREATE VIEW [dbo].[v_Person]
AS
SELECT [SomeId]
,[CreatedByUserId]
,[CreatedUtcDate]
,[RowVersion]
,CONVERT(NVARCHAR(50),DECRYPTBYKEY([FirstName])) [FirstName]
,CONVERT(NVARCHAR(50),DECRYPTBYKEY([LastName])) [LastName]
FROM [dbo].[Person]
第2步 - 创建域模型Person类,其中string
为相关属性类型,而不是byte[]
(请注意select
中的View
语句1}}我们将解密的列转换为NVARCHAR
)。
public class Person
{
public int SomeId { get; set; }
public string FirstName { get; set; } // string, not binary
public string LastName { get; set; } // string, not binary
public Guid CreatedByUserId { get; set; }
public DateTime CreatedUtcDate { get; set; }
public int SomeForeignKeyId { get; set; }
}
第3步 - 我们需要为该Domain类设置映射。 (我在这里提出的解决方案是针对EF6。我知道EF Core还不支持单独的映射文件,所以这需要在DbContext的OnModelCreating事件中完成)。为您的域对象创建一个映射类,如下所示:
public class PersonMap : EntityTypeConfiguration<Person>
{
public PersonConfiguration(string schema)
{
ToTable("v_Person", schema); // note we map to the View
HasKey(x => x.SomeId);
// ... other properties elided for brevity
Property(x => x.FirstName)
.HasColumnName(@"FirstName")
.HasColumnType("nvarchar")
.IsOptional()
.HasMaxLength(50);
Property(x => x.LastName)
.HasColumnName(@"LastName")
.HasColumnType("nvarchar")
.IsOptional()
.HasMaxLength(50);
// Foreign keys
HasRequired(a => a.LogbookEntry)
.WithOptional(b => b.Person)
.WillCascadeOnDelete(false);
MapToStoredProcedures(p =>
p.Insert(i => i.HasName("Insert_Person"))
.Update(u => u.HasName("Update_Person"))
.Delete(d => d.HasName("Delete_Person")));
}
}
注意我们如何映射到视图v_Person
,而不是原始表
另请注意MapToStoredProcedures
的来电,我将在下面解释。
第4步 - 最后一步是为Insert,Update和Deletes创建一些存储过程。当您调用SaveChanges时,这些将由EF调用,并且将根据实体具有的EntityState
调用相关的存储过程。我不会列出所有3,但Update
存储过程的示例可能类似于:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[Update_Person]
@SomeId INT,
@CreatedByUserId UNIQUEIDENTIFIER,
@CreatedUtcDate DATETIME,
@RowVersion_Original timestamp,
@FirstName NVARCHAR(50),
@LastName NVARCHAR(50) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE @CertKey NVARCHAR(7) = 'CertKey';
UPDATE PersonDetail
SET
FirstName = ENCRYPTBYKEY(KEY_GUID(@CertKey), @FirstName),
LastName = ENCRYPTBYKEY(KEY_GUID(@CertKey), @LastName)
WHERE SomeId = @SomeId
SELECT SomeId, RowVersion
FROM PersonDetail
WHERE SomeId = @SomeId
END
如果你做得更好,请随意发表评论 干杯