Приступая к работе с API защиты данных в ASP.NET Core

По сути, защита данных состоит из следующих этапов.

  1. Создайте средство защиты данных от поставщика защиты данных.
  2. Вызовите Protect метод с данными, которые необходимо защитить.
  3. Вызовите Unprotect метод с данными, которые необходимо вернуть в обычный текст.

большинство платформ и моделей приложений, таких как ASP.NET Core или SignalR , уже настраивают систему защиты данных и добавляют их в контейнер службы, доступ к которому осуществляется посредством 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", не сможет снять защиту данных, предоставляемых предохранителем, с целью "фиолетовый".

Совет

Экземпляры IDataProtectionProvider и IDataProtector являются потокобезопасными для нескольких вызывающих объектов. Предполагается, что после того, как компонент получает ссылку на объект IDataProtector через вызов CreateProtector , он будет использовать эту ссылку для нескольких вызовов функций Protect и Unprotect .

Вызов метода вызовет Unprotect исключение CryptographicException, если защищенные полезные данные не могут быть проверены или расшифрованы. Некоторым компонентам может потребоваться пропускать ошибки во время операций снятия защиты. компонент, считывающий проверку подлинности cookie , может обработать эту ошибку и обрабатывать запрос так, как если бы он имел вообще не cookie запрашивать ошибку. Компоненты, которые должны использовать это поведение, должны специально перехватывать CryptographicException, а не проглатывание все исключения.

Использование Аддоптионс для настройки пользовательского репозитория

Рассмотрим следующий код, который использует поставщик услуг, поскольку реализация IXmlRepository имеет зависимость от одноэлементной службы:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    var sp = services.BuildServiceProvider();
    services.AddDataProtection()
      .AddKeyManagementOptions(o => o.XmlRepository = sp.GetService<IXmlRepository>());
}

Приведенный выше код регистрирует следующее предупреждение:

Вызов "Буилдсервицепровидер" из кода приложения приводит к созданию дополнительной копии создаваемых одноэлементных служб. В качестве параметров для 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();
        }
    }
}

В следующем коде показан класс Ксмлкэй:

public class XmlKey
{
    public Guid Id { get; set; }
    public string Xml { get; set; }

    public XmlKey()
    {
        this.Id = Guid.NewGuid();
    }
}