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();
}
}