Wprowadzenie do interfejsów API ochrony danych w usłudze ASP.NET Core

Zasadniczo ochrona danych składa się z następujących kroków:

  1. Utwórz ochronę danych od dostawcy ochrony danych.
  2. Wywołaj metodę przy Protect użyciu danych, które chcesz chronić.
  3. Wywołaj metodę przy Unprotect użyciu danych, które chcesz przywrócić do zwykłego tekstu.

Większość struktur i modeli aplikacji, takich jak ASP.NET Core lub SignalR, już konfiguruje system ochrony danych i dodaje go do kontenera usługi, który jest dostępny za pośrednictwem wstrzykiwania zależności. W poniższym przykładzie pokazano:

  • Konfigurowanie kontenera usługi na potrzeby wstrzykiwania zależności i rejestrowania stosu ochrony danych.
  • Odbieranie dostawcy ochrony danych za pośrednictwem di.
  • Tworzenie funkcji ochrony.
  • Następnie ochrona danych jest niechroniająca.
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!
 */

Podczas tworzenia funkcji ochrony należy podać co najmniej jeden ciąg przeznaczenia. Ciąg przeznaczenia zapewnia izolację między użytkownikami. Na przykład funkcja ochrony utworzona z ciągiem przeznaczenia "zielony" nie będzie mogła wyłączyć ochrony danych dostarczonych przez ochronę z celem "purpurowy".

Napiwek

IDataProtectionProvider Wystąpienia elementów i IDataProtector są bezpieczne wątkowo dla wielu wywołujących. Ma to na celu, że gdy składnik pobiera odwołanie do elementu IDataProtector za pośrednictwem wywołania CreateProtectormetody , będzie używać tego odwołania dla wielu wywołań do Protect i Unprotect.

Wywołanie metody , aby zgłosić Unprotect wyjątek CryptographicException, jeśli nie można zweryfikować ani odszyfrować chronionego ładunku. Niektóre składniki mogą chcieć ignorować błędy podczas operacji usuwania ochrony; składnik, który odczytuje uwierzytelnianie cookie, może obsłużyć ten błąd i traktować żądanie tak, jakby w ogóle nie cookie miał, zamiast nie prowadzić żądania wprost. Składniki, które chcą tego zachowania, powinny przechwytywać wyjątek CryptographicException zamiast połykać wszystkie wyjątki.

Konfigurowanie repozytorium niestandardowego za pomocą polecenia AddOptions

Rozważ następujący kod, który używa dostawcy usług, ponieważ implementacja IXmlRepository programu ma zależność od pojedynczej usługi:

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

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

Powyższy kod rejestruje następujące ostrzeżenie:

Wywołanie metody "BuildServiceProvider" z kodu aplikacji powoduje utworzenie dodatkowej kopii usług singleton. Rozważ alternatywy, takie jak wstrzykiwanie usług zależności jako parametry do "Konfiguruj".

Poniższy kod zapewnia implementację IXmlRepository bez konieczności kompilowania dostawcy usług, a tym samym tworzenia dodatkowych kopii pojedynczych usług:

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

Powyższy kod usuwa wywołanie metody GetService i ukrywa IConfigureOptions<T>element .

Poniższy kod przedstawia niestandardowe repozytorium 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();
        }
    }
}

Poniższy kod przedstawia klasę XmlKey:

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

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