Começar a trabalhar com as APIs de Proteção de Dados ASP.NET Core

Basicamente, a proteção de dados consiste nas seguintes etapas:

  1. Crie um protetor de dados de um provedor de proteção de dados.
  2. Chame o Protect método com os dados que você deseja proteger.
  3. Chame o Unprotect método com os dados que você deseja transformar novamente em texto sem-texto.

A maioria das estruturas e modelos de aplicativo, como ASP.NET Core ou , já configura o sistema de proteção de dados e o adiciona a um contêiner de serviço acessado por meio da injeção SignalR de dependência. O exemplo a seguir demonstra:

  • Configurar um contêiner de serviço para injeção de dependência e registrar a pilha de proteção de dados.
  • Recebendo o provedor de proteção de dados por meio da DI.
  • Criando um protetor.
  • Proteger e desproteger dados.
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!
 */

Ao criar um protetor, você deve fornecer uma ou mais Cadeias de Caracteres de Finalidade. Uma cadeia de caracteres de finalidade fornece isolamento entre os consumidores. Por exemplo, um protetor criado com uma cadeia de caracteres de finalidade "verde" não seria capaz de desproteger dados fornecidos por um protetor com a finalidade de "roxo".

Dica

As instâncias IDataProtectionProvider de e IDataProtector são thread-safe para vários chamadores. Destina-se a que, depois que um componente obtém uma referência a um por meio de uma chamada para , ele usará essa referência para várias IDataProtector CreateProtector chamadas para e Protect Unprotect .

Uma chamada para Unprotect lançará CryptographicException se o conteúdo protegido não puder ser verificado ou desbloqueado. Alguns componentes podem querer ignorar erros durante operações de desproteger; um componente que lê a autenticação s pode tratar esse erro e tratar a solicitação como se ela não tivesse nenhum, em vez de falhar cookie cookie a solicitação de imediato. Os componentes que querem esse comportamento devem capturar especificamente CryptographicException em vez de ressarciá-los.

Usar AddOptions para configurar o repositório personalizado

Considere o seguinte código que usa um provedor de serviços porque a implementação do IXmlRepository tem uma dependência em um serviço singleton:

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

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

O código anterior registra o seguinte aviso:

Chamar 'BuildServiceProvider' do código do aplicativo resulta em uma cópia adicional dos serviços singleton que estão sendo criados. Considere alternativas como a injeção de dependência de serviços como parâmetros para "Configurar".

O código a seguir fornece a implementação sem precisar criar o provedor de serviços IXmlRepository e, portanto, fazer cópias adicionais de serviços singleton:

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

O código anterior remove a chamada para GetService e oculta IConfigureOptions<T> .

O código a seguir mostra o repositório XML personalizado:

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

O código a seguir mostra a classe XmlKey:

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

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