Integrujte MSAL.NET s Microsoft. Identity.Web v .NET Frameworku

V této příručce se dozvíte, jak používat tokenovou mezipaměť Microsoft.Identity.Web a balíčky certifikátů s MSAL.NET v rozhraní .NET Framework, .NET Standard 2.0 a v klasických aplikacích .NET (.NET 4.7.2+).

Vysvětlení přehledu

Od verze Microsoft.Identity.Web 1.17+ můžete použít nástrojové balíčky Microsoft.Identity.Web s MSAL.NET v ne-ASP.NET Core prostředích.

Identifikace výhod balíčku

funkce Prospěch
Serializace mezipaměti tokenů Opakovaně použitelné adaptéry mezipaměti pro paměťovou cache, SQL Server, Redis, Cosmos DB, PostgreSQL
Pomocníci certifikátů Zjednodušené načítání certifikátů z úložišť klíčů KeyVault, systému souborů nebo certifikátů
Rozšíření nároků Pomocné metody pro ClaimsPrincipal manipulaci
.NET Standard 2.0 Kompatibilní s rozhraním .NET Framework 4.7.2+, .NET Core a .NET 5 nebo novějším
Minimální závislosti Cílové balíčky bez ASP.NET Core závislostí

Kontrola podporovaných scénářů

Následující scénáře jsou podporovány cílovými balíčky nástrojů.

  • .NET Framework Console Applications (scénáře démona)
  • Desktop Applications (.NET Framework)
  • Worker Services (.NET Framework)
  • knihovny .NET Standard 2.0 (kompatibilita mezi platformami)
  • Newebové MSAL.NET aplikace

Poznámka:

Informace o aplikacích ASP.NET MVC/Web API najdete v tématu OWIN Integration.


Výběr balíčků

Zvolte balíček, který odpovídá vašemu scénáři.

Identifikace základních balíčků pro MSAL.NET

Balíček Purpose Závislosti cíl .NET
Microsoft. Identity.Web.TokenCache Serializátory mezipaměti tokenů, ClaimsPrincipal rozšíření Minimální .NET Standard 2.0
Microsoft. Identity.Web.Certificate Nástroje pro načítání certifikátů Minimální .NET Standard 2.0

Instalace balíčků

K přidání balíčků do projektu použijte jednu z následujících metod.

Správce balíčků Console:

# Token cache serialization
Install-Package Microsoft.Identity.Web.TokenCache

# Certificate management
Install-Package Microsoft.Identity.Web.Certificate

.NET CLI:

dotnet add package Microsoft.Identity.Web.TokenCache
dotnet add package Microsoft.Identity.Web.Certificate

Vysvětlení omezení základních balíčků

Základní balíček Microsoft.Identity.Web zahrnuje ASP.NET Core závislosti (Microsoft.AspNetCore.*), které:

  • Nekompatibilní s rozhraním ASP.NET Framework
  • Zbytečně zvětšovat velikost balíčku
  • Vytváření konfliktů závislostí

Použijte raději cílové balíčky pro scénáře .NET Framework a .NET Standard.


Konfigurace serializace mezipaměti tokenů

Porozumění adaptérům mezipaměti tokenů

Microsoft.Identity.Web poskytuje adaptéry mezipaměti tokenů, které bezproblémově fungují s IConfidentialClientApplication MSAL.NET.

Vytvoření důvěrného klienta s mezipamětí tokenů

Následující příklad vytvoří důvěrnou klientskou aplikaci a připojí mezipaměť tokenů v paměti.

using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders;

public class MsalAppBuilder
{
    private static IConfidentialClientApplication _app;

    public static IConfidentialClientApplication BuildConfidentialClientApplication()
    {
        if (_app == null)
        {
            string clientId = ConfigurationManager.AppSettings["AzureAd:ClientId"];
            string clientSecret = ConfigurationManager.AppSettings["AzureAd:ClientSecret"];
            string tenantId = ConfigurationManager.AppSettings["AzureAd:TenantId"];

            // Create the confidential client application
            _app = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithClientSecret(clientSecret)
                .WithTenantId(tenantId)
                .WithAuthority(AzureCloudInstance.AzurePublic, tenantId)
                .Build();

            // Add token cache serialization (choose one option below)
            _app.AddInMemoryTokenCache();
        }

        return _app;
    }
}

Volba možností mezipaměti tokenů

Vyberte poskytovatele mezipaměti, který nejlépe vyhovuje vašemu scénáři nasazení.

Konfigurace mezipaměti tokenů v paměti

Následující příklad přidá jednoduchou mezipaměť v paměti:

using Microsoft.Identity.Web.TokenCacheProviders;

_app.AddInMemoryTokenCache();

Mezipaměť v paměti s omezením velikosti (Microsoft.Identity.Web 1.20+):

using Microsoft.Extensions.Caching.Memory;

_app.AddInMemoryTokenCache(services =>
{
    // Configure memory cache options
    services.Configure<MemoryCacheOptions>(options =>
    {
        options.SizeLimit = 5000000;  // 5 MB limit
    });
});

Charakteristiky:

  • Rychlý přístup
  • Žádné externí závislosti
  • Není sdílený mezi procesy
  • Ztráta při restartování aplikace

Případ použití: Aplikace konzoly s jednou instancí, desktopové aplikace


Konfigurace mezipaměti tokenů distribuované v paměti

Pomocí následujícího kódu přidejte distribuovanou mezipaměť v paměti pro prostředí s více instancemi:

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.Memory (NuGet)
    services.AddDistributedMemoryCache();
});

Charakteristiky:

  • Sdílené mezi instancemi aplikací
  • Lepší pro scénáře s vyrovnáváním zatížení
  • Vyžaduje další balíček NuGet.
  • Stále při restartování aplikace dochází ke ztrátě

Případ použití: Služby s více instancemi s přijatelným opětovným získáním tokenu


Konfigurace mezipaměti tokenů SQL Server

Pomocí následujícího kódu přidejte trvalou distribuovanou mezipaměť SQL Server:

using Microsoft.Extensions.Caching.SqlServer;

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.SqlServer (NuGet)
    services.AddDistributedSqlServerCache(options =>
    {
        options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
        options.SchemaName = "dbo";
        options.TableName = "TokenCache";

        // IMPORTANT: Set expiration above token lifetime
        // Access tokens typically expire after 1 hour
        options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
    });
});

Spuštěním následujícího příkazu SQL vytvořte požadovanou tabulku mezipaměti:

-- Create the cache table
CREATE TABLE [dbo].[TokenCache] (
    [Id] NVARCHAR(449) NOT NULL,
    [Value] VARBINARY(MAX) NOT NULL,
    [ExpiresAtTime] DATETIMEOFFSET NOT NULL,
    [SlidingExpirationInSeconds] BIGINT NULL,
    [AbsoluteExpiration] DATETIMEOFFSET NULL,
    PRIMARY KEY ([Id])
);

-- Create index for performance
CREATE INDEX [Index_ExpiresAtTime] ON [dbo].[TokenCache] ([ExpiresAtTime]);

Charakteristiky:

  • Trvalé při restartování
  • Sdíleno napříč několika instancemi
  • Spolehlivé a škálovatelné
  • Vyžaduje nastavení SQL Server

Případ použití: Produkční démonové služby, naplánované úlohy, pracovníci s více instancemi


Konfigurace mezipaměti tokenů Redis

Pomocí následujícího kódu přidejte vysoce výkonnou distribuovanou mezipaměť Redis:

using StackExchange.Redis;
using Microsoft.Extensions.Caching.StackExchangeRedis;

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.StackExchangeRedis (NuGet)
    services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = ConfigurationManager.AppSettings["Redis:ConnectionString"];
        options.InstanceName = "TokenCache_";
    });
});

Následující příklad ukazuje konfiguraci Redis připravenou pro produkční prostředí:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = ConfigurationManager.AppSettings["Redis:ConnectionString"];
    options.InstanceName = "MyDaemonApp_";

    // Optional: Configure Redis options
    options.ConfigurationOptions = new ConfigurationOptions
    {
        AbortOnConnectFail = false,
        ConnectTimeout = 5000,
        SyncTimeout = 5000
    };
});

Charakteristiky:

  • Extrémně rychlá
  • Sdíleno mezi instancemi
  • Trvalá (s povolenou persistentností Redis)
  • Vyžaduje server Redis.

Případ použití: Vysokoobjemové daemon aplikace, distribuované systémy, mikroslužby


Konfigurace mezipaměti tokenů Cosmos DB

Pomocí následujícího kódu přidejte globálně distribuovanou mezipaměť Cosmos DB:

using Microsoft.Extensions.Caching.Cosmos;

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.Cosmos (preview)
    services.AddCosmosCache(options =>
    {
        options.ContainerName = "TokenCache";
        options.DatabaseName = "IdentityCache";
        options.ClientBuilder = new CosmosClientBuilder(
            ConfigurationManager.AppSettings["CosmosConnectionString"]);
        options.CreateIfNotExists = true;
    });
});

Charakteristiky:

  • Globálně distribuovaná
  • Vysoká dostupnost
  • Automatické škálování
  • Vyšší latence než Redis
  • Vyšší náklady

Případ použití: Globální služby démona, geograficky distribuované aplikace


Konfigurace mezipaměti tokenů PostgreSQL

K přidání distribuované mezipaměti PostgreSQL použijte následující kód:

_app.AddDistributedTokenCaches(services =>
{
    // Requires: Microsoft.Extensions.Caching.Postgres (NuGet)
    services.AddDistributedPostgresCache(options =>
    {
        options.ConnectionString = ConfigurationManager.ConnectionStrings["PostgresCache"].ConnectionString;
        options.SchemaName = ConfigurationManager.AppSettings["PostgresCache:SchemaName"];
        options.TableName = ConfigurationManager.AppSettings["PostgresCache:TableName"];
        options.CreateIfNotExists = bool.Parse(
            ConfigurationManager.AppSettings["PostgresCache:CreateIfNotExists"] ?? "true");

        // Set expiration above token lifetime.
        // Access tokens typically expire after 1 hour.
        options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
    });
});

Charakteristiky:

  • Trvalé při restartování
  • Sdíleno napříč několika instancemi
  • Známá sémantika SQL
  • Funguje s Azure Database for PostgreSQL
  • Vyžaduje server PostgreSQL.

Použití případu: Aplikace, které už jako primární databázi používají PostgreSQL, nebo služby hostované Azure pomocí Azure Database for PostgreSQL


Vytvořte kompletní aplikaci démona

Následující příklad ukazuje úplnou aplikaci démona, která získává tokeny pomocí přihlašovacích údajů klienta a mezipaměti tokenů SQL Server.

using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders;
using System;
using System.Threading.Tasks;

namespace DaemonApp
{
    class Program
    {
        private static IConfidentialClientApplication _app;

        static async Task Main(string[] args)
        {
            // Build confidential client with token cache
            _app = BuildConfidentialClient();

            // Acquire token for app-only access
            string[] scopes = new[] { "https://graph.microsoft.com/.default" };

            try
            {
                var result = await _app.AcquireTokenForClient(scopes)
                    .ExecuteAsync();

                Console.WriteLine($"Token acquired successfully!");
                Console.WriteLine($"Token source: {result.AuthenticationResultMetadata.TokenSource}");
                Console.WriteLine($"Expires on: {result.ExpiresOn}");

                // Use token to call API
                await CallProtectedApi(result.AccessToken);
            }
            catch (MsalServiceException ex)
            {
                Console.WriteLine($"Error acquiring token: {ex.ErrorCode}");
                Console.WriteLine($"CorrelationId: {ex.CorrelationId}");
            }
        }

        private static IConfidentialClientApplication BuildConfidentialClient()
        {
            var app = ConfidentialClientApplicationBuilder
                .Create(ConfigurationManager.AppSettings["ClientId"])
                .WithClientSecret(ConfigurationManager.AppSettings["ClientSecret"])
                .WithTenantId(ConfigurationManager.AppSettings["TenantId"])
                .Build();

            // Add SQL Server token cache for persistence
            app.AddDistributedTokenCaches(services =>
            {
                services.AddDistributedSqlServerCache(options =>
                {
                    options.ConnectionString = ConfigurationManager
                        .ConnectionStrings["TokenCache"].ConnectionString;
                    options.SchemaName = "dbo";
                    options.TableName = "TokenCache";
                    options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
                });
            });

            return app;
        }

        private static async Task CallProtectedApi(string accessToken)
        {
            // Your API call logic
        }
    }
}

Správa certifikátů

Principy načítání certifikátů

Microsoft. Identity.Web zjednodušuje načítání certifikátů z různých zdrojů pro toky přihlašovacích údajů klienta.

Načtení certifikátů pomocí DefaultCertificateLoader

Následující příklad ukazuje, jak načíst certifikát z Azure Key Vault a vytvořit důvěrnou klientskou aplikaci.

using Microsoft.Identity.Web;
using Microsoft.Identity.Client;

public class CertificateHelper
{
    public static IConfidentialClientApplication CreateAppWithCertificate()
    {
        string clientId = ConfigurationManager.AppSettings["AzureAd:ClientId"];
        string tenantId = ConfigurationManager.AppSettings["AzureAd:TenantId"];

        // Define certificate source
        var certDescription = CertificateDescription.FromKeyVault(
            keyVaultUrl: "https://my-keyvault.vault.azure.net",
            keyVaultCertificateName: "MyCertificate"
        );

        // Load certificate
        ICertificateLoader certificateLoader = new DefaultCertificateLoader();
        certificateLoader.LoadIfNeeded(certDescription);

        // Create confidential client with certificate
        var app = ConfidentialClientApplicationBuilder.Create(clientId)
            .WithCertificate(certDescription.Certificate)
            .WithTenantId(tenantId)
            .Build();

        // Add token cache
        app.AddInMemoryTokenCache();

        return app;
    }
}

Volba zdrojů certifikátů

Načtení z Azure Key Vault

Načtěte certifikát uložený v Azure Key Vault zadáním adresy URL trezoru a názvu certifikátu.

var certDescription = CertificateDescription.FromKeyVault(
    keyVaultUrl: "https://my-keyvault.vault.azure.net",
    keyVaultCertificateName: "MyApplicationCert"
);

ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);

var app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithCertificate(certDescription.Certificate)
    .WithTenantId(tenantId)
    .Build();

Požadavky:

  • Spravovaná identita nebo instanční objekt s přístupem Key Vault
  • balíček NuGet Azure.Identity
  • oprávnění Key Vault: Get u certifikátů

Načtení z úložiště certifikátů

Načtěte certifikát z úložiště certifikátů Windows podle jednoznačného názvu.

var certDescription = CertificateDescription.FromStoreWithDistinguishedName(
    distinguishedName: "CN=MyApp.contoso.com",
    storeName: StoreName.My,
    storeLocation: StoreLocation.CurrentUser
);

ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);

var app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithCertificate(certDescription.Certificate)
    .WithTenantId(tenantId)
    .Build();

Certifikát můžete najít také kryptografickým otiskem:

var certDescription = CertificateDescription.FromStoreWithThumbprint(
    thumbprint: "ABCDEF1234567890ABCDEF1234567890ABCDEF12",
    storeName: StoreName.My,
    storeLocation: StoreLocation.LocalMachine
);

Načtení ze systému souborů

Načtěte certifikát ze souboru PFX v místním systému souborů.

var certDescription = CertificateDescription.FromPath(
    path: @"C:\Certificates\MyAppCert.pfx",
    password: ConfigurationManager.AppSettings["Certificate:Password"]
);

ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);

var app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithCertificate(certDescription.Certificate)
    .WithTenantId(tenantId)
    .Build();

Poznámka k zabezpečení: Nikdy nezakódujte hesla. Použijte zabezpečenou konfiguraci.


Načíst z Base64 kódovaného řetězce

Načtěte certifikát z řetězce zakódovaného v Base64 uloženého v konfiguraci.

string base64Cert = ConfigurationManager.AppSettings["Certificate:Base64"];

var certDescription = CertificateDescription.FromBase64Encoded(
    base64EncodedValue: base64Cert,
    password: ConfigurationManager.AppSettings["Certificate:Password"]  // Optional
);

ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certDescription);

Konfigurace načítání certifikátů z App.config

Definujte nastavení certifikátu v souboru App.config a načtěte je za běhu.

App.config:

<appSettings>
  <add key="AzureAd:ClientId" value="your-client-id" />
  <add key="AzureAd:TenantId" value="your-tenant-id" />

  <!-- Option 1: KeyVault -->
  <add key="Certificate:SourceType" value="KeyVault" />
  <add key="Certificate:KeyVaultUrl" value="https://my-vault.vault.azure.net" />
  <add key="Certificate:KeyVaultCertificateName" value="MyCert" />

  <!-- Option 2: Store -->
  <!--
  <add key="Certificate:SourceType" value="StoreWithThumbprint" />
  <add key="Certificate:CertificateThumbprint" value="ABCD..." />
  <add key="Certificate:CertificateStorePath" value="CurrentUser/My" />
  -->
</appSettings>

<connectionStrings>
  <add name="TokenCache"
       connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=TokenCache;Integrated Security=True;" />
</connectionStrings>

K načtení certifikátu na základě konfigurace použijte následující pomocnou metodu:

public static CertificateDescription GetCertificateFromConfig()
{
    string sourceType = ConfigurationManager.AppSettings["Certificate:SourceType"];

    return sourceType switch
    {
        "KeyVault" => CertificateDescription.FromKeyVault(
            ConfigurationManager.AppSettings["Certificate:KeyVaultUrl"],
            ConfigurationManager.AppSettings["Certificate:KeyVaultCertificateName"]
        ),

        "StoreWithThumbprint" => CertificateDescription.FromStoreWithThumbprint(
            ConfigurationManager.AppSettings["Certificate:CertificateThumbprint"],
            StoreName.My,
            StoreLocation.CurrentUser
        ),

        _ => throw new ConfigurationErrorsException("Invalid certificate source type")
    };
}

Prozkoumání ukázkových aplikací

Projděte si tyto ukázky a podívejte se na funkční implementace.

Projděte si oficiální ukázky Microsoft

Následující tabulka uvádí oficiální ukázky, které demonstrují ukládání tokenů do mezipaměti a načítání certifikátů.

Ukázka Platforma Description
ConfidentialClientTokenCache Konzola (.NET Framework) Vzory serializace mezipaměti tokenů
active-directory-dotnetcore-daemon-v2 Konzola (.NET Core) Načítání certifikátů z Key Vault

Dodržujte osvědčené postupy.

Tyto vzory použijte k vytváření spolehlivých a zabezpečených aplikací.

1. Použijte singleton vzor pro IConfidentialClientApplication:

Vytvořte jednu instanci a znovu ji použijte v celé aplikaci.

private static IConfidentialClientApplication _app;

public static IConfidentialClientApplication GetApp()
{
    if (_app == null)
    {
        _app = ConfidentialClientApplicationBuilder.Create(clientId)
            .WithClientSecret(clientSecret)
            .WithTenantId(tenantId)
            .Build();

        _app.AddDistributedTokenCaches(/* ... */);
    }

    return _app;
}

2. Nastavte vypršení platnosti příslušné mezipaměti tokenů:

Nakonfigurujte posuvné vypršení platnosti nad životností tokenu, aby se zabránilo zbytečnému opětovnému získání.

// Access tokens typically expire after 1 hour
// Set cache expiration ABOVE token lifetime
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);

3. Použijte zabezpečené úložiště certifikátů:

Ukládejte certifikáty do Azure Key Vault nebo do správně zabezpečeného úložiště certifikátů.

// Azure Key Vault (production)
var cert = CertificateDescription.FromKeyVault(keyVaultUrl, certName);

// Certificate store with proper permissions
var cert = CertificateDescription.FromStoreWithThumbprint(
    thumbprint, StoreName.My, StoreLocation.LocalMachine);

4. Implementace správného zpracování chyb:

Zachyťte výjimky MSAL a zalogujte ID korelace pro účely řešení problémů.

try
{
    var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
}
catch (MsalServiceException ex)
{
    logger.Error($"Token acquisition failed. CorrelationId: {ex.CorrelationId}, ErrorCode: {ex.ErrorCode}");
    throw;
}

5. Použijte distribuovanou mezipaměť pro produkční prostředí:

Distribuovaná mezipaměť sdílí tokeny mezi instancemi a zachovává je i po restartování.

// Correct for daemon services
app.AddDistributedTokenCaches(services =>
{
    services.AddDistributedSqlServerCache(/* ... */);
});

Vyhněte se běžným chybám

1. Nevytvávejte nové instance IConfidentialClientApplication opakovaně:

// Wrong - creates new instance every time
public void AcquireToken()
{
    var app = ConfidentialClientApplicationBuilder.Create(clientId).Build();
    // ...
}

// Correct - use singleton
private static readonly IConfidentialClientApplication _app = BuildApp();

2. Nezakódujte tajné kódy:

// Wrong
.WithClientSecret("supersecretvalue123")

// Correct
.WithClientSecret(ConfigurationManager.AppSettings["AzureAd:ClientSecret"])

3. Nepoužívejte mezipaměť v paměti pro služby s více instancemi:

// Wrong for services with multiple instances
app.AddInMemoryTokenCache();

// Correct - use distributed cache
app.AddDistributedTokenCaches(services =>
{
    services.AddDistributedSqlServerCache(/* ... */);
});

4. Neignorujte ověření certifikátu:

// Wrong - skips validation
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, errors) => true;

// Correct - validate certificates properly

Migrace z ADAL.NET

Projděte si klíčové rozdíly a aktualizujte kód tak, aby používal MSAL.NET s Microsoft. Identity.Web.

Vysvětlení klíčových rozdílů

Aspekt ADAL.NET (zastaralé) MSAL.NET + Microsoft. Identity.Web
Rozsahy Založené na prostředcích (https://graph.microsoft.com) Založené na rozsahu (https://graph.microsoft.com/.default)
Mezipaměť tokenů Vyžaduje se ruční serializace Integrované adaptéry prostřednictvím rozšiřujících metod
Certifikáty Ruční načítání X509Certificate2 DefaultCertificateLoader s více zdroji
Autorita Pevně nastaveno při výstavbě Je možné přepsat na požadavek.

Porovnání příkladů migrace

DAL.NET (starý):

AuthenticationContext authContext = new AuthenticationContext(authority);
ClientCredential credential = new ClientCredential(clientId, clientSecret);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, credential);

MSAL.NET s Microsoft. Identity.Web (nový):

var app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithClientSecret(clientSecret)
    .WithTenantId(tenantId)
    .Build();

app.AddInMemoryTokenCache();  // Add token cache

string[] scopes = new[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = await app.AcquireTokenForClient(scopes).ExecuteAsync();

Další informace o souvisejících scénářích najdete v těchto zdrojích informací.