在 ASP.NET Core 中開始使用資料保護 API
基本上,保護資料是由下列步驟所組成:
- 從資料保護提供者建立資料保護程式。
- 使用您想要保護的資料,呼叫
Protect
方法。 - 使用您想要重新轉換成純文字的資料呼叫
Unprotect
方法。
大部分的架構和應用程式模型,例如 ASP.NET Core 或 SignalR,都已經設定資料保護系統,並將它新增至透過相依性插入存取的服務容器。 下列範例示範:
- 設定服務容器以進行相依性插入和註冊資料保護堆疊。
- 透過 DI 接收資料保護提供者。
- 建立保護程式。
- 保護然後解除保護資料。
using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
public class Program
{
public static void Main(string[] args)
{
// add data protection services
var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection();
var services = serviceCollection.BuildServiceProvider();
// create an instance of MyClass using the service provider
var instance = ActivatorUtilities.CreateInstance<MyClass>(services);
instance.RunSample();
}
public class MyClass
{
IDataProtector _protector;
// the 'provider' parameter is provided by DI
public MyClass(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector("Contoso.MyClass.v1");
}
public void RunSample()
{
Console.Write("Enter input: ");
string input = Console.ReadLine();
// protect the payload
string protectedPayload = _protector.Protect(input);
Console.WriteLine($"Protect returned: {protectedPayload}");
// unprotect the payload
string unprotectedPayload = _protector.Unprotect(protectedPayload);
Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
}
}
}
/*
* SAMPLE OUTPUT
*
* Enter input: Hello world!
* Protect returned: CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ
* Unprotect returned: Hello world!
*/
當您建立保護程式時,必須提供一或多個目的字串。 目的字串提供取用者之間的隔離。 例如,以 "green" 目的字串建立的保護程式,將無法解除保護程式以 "purple" 為目的所提供的資料。
提示
IDataProtectionProvider
和 IDataProtector
的執行個體對多個呼叫而言是安全執行緒。 一旦元件透過呼叫 CreateProtector
取得對 IDataProtector
的參考,就會將該參考使用於對 Protect
和 Unprotect
的多次呼叫。
如果無法驗證或解碼受保護的承載,則對 Unprotect
的呼叫將會擲回 CryptographicException。 某些元件可能會想要在解除保護作業期間忽略錯誤;讀取驗證 Cookie 的元件可能會處理此錯誤,並將要求視為完全沒有 cookie,而不是完全失敗要求。 想要此行為的元件應該特別攔截 CryptographicException,而不是吞沒所有例外狀況。
使用 AddOptions 設定自訂存放庫
請考慮使用服務提供者的下列程式碼,因為實作 IXmlRepository
與單一服務有相依性:
public void ConfigureServices(IServiceCollection services)
{
// ...
var sp = services.BuildServiceProvider();
services.AddDataProtection()
.AddKeyManagementOptions(o => o.XmlRepository = sp.GetService<IXmlRepository>());
}
上述程式碼會記錄下列警告:
從應用程式程式碼呼叫 'BuildServiceProvider' 會導致建立單一服務的額外複本。 考慮替代方法,例如相依性插入服務做為 'Configure' 的參數。
下列程式碼會提供 IXmlRepository
實作,而不需要建置服務提供者,因而製作單一服務的其他複本:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataProtectionDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
// Register XmlRepository for data protection.
services.AddOptions<KeyManagementOptions>()
.Configure<IServiceScopeFactory>((options, factory) =>
{
options.XmlRepository = new CustomXmlRepository(factory);
});
services.AddRazorPages();
}
上述程式碼會移除對 GetService
的呼叫,並隱藏 IConfigureOptions<T>
。
下列程式碼顯示自訂 XML 存放庫:
using CustomXMLrepo.Data;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
public class CustomXmlRepository : IXmlRepository
{
private readonly IServiceScopeFactory factory;
public CustomXmlRepository(IServiceScopeFactory factory)
{
this.factory = factory;
}
public IReadOnlyCollection<XElement> GetAllElements()
{
using (var scope = factory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<DataProtectionDbContext>();
var keys = context.XmlKeys.ToList()
.Select(x => XElement.Parse(x.Xml))
.ToList();
return keys;
}
}
public void StoreElement(XElement element, string friendlyName)
{
var key = new XmlKey
{
Xml = element.ToString(SaveOptions.DisableFormatting)
};
using (var scope = factory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<DataProtectionDbContext>();
context.XmlKeys.Add(key);
context.SaveChanges();
}
}
}
下列程式碼顯示 XmlKey 類別:
public class XmlKey
{
public Guid Id { get; set; }
public string Xml { get; set; }
public XmlKey()
{
this.Id = Guid.NewGuid();
}
}