Delen via


Handleiding: Gebruikers authenticeren voor uw WPF-desktopapplicatie

Van toepassing op: Witte cirkel met een grijs X-symbool. Werknemershuurders Groene cirkel met een wit vinkje. Externe huurders (lees meer)

In deze zelfstudie ziet u hoe u een WPF-bureaublad-app (Windows Presentation Form) bouwt en voorbereidt op verificatie met behulp van het Microsoft Entra-beheercentrum.

In deze handleiding leert u:

  • Configureer een WPF-desktopapplicatie om de registratiegegevens van de app te gebruiken.
  • Bouw een desktop-app die zich aanmeldt bij een gebruiker en een token namens de gebruiker verkrijgt.

Vereiste voorwaarden

Een WPF-bureaubladtoepassing maken

  1. Open de terminal en navigeer naar de map waarin u het project wilt opslaan.

  2. Initialiseer een WPF-bureaublad-app en navigeer naar de hoofdmap.

    dotnet new wpf --language "C#" --name sign-in-dotnet-wpf
    cd sign-in-dotnet-wpf
    

Pakketten installeren

Installeer configuratieproviders die onze app helpen bij het lezen van configuratiegegevens van sleutel-waardeparen in ons app-instellingenbestand. Deze configuratieabstracties bieden de mogelijkheid om configuratiewaarden te binden aan exemplaren van .NET-objecten.

dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Configuration.Binder

Installeer de Microsoft Authentication Library (MSAL) die alle belangrijke onderdelen bevat die u nodig hebt om een token te verkrijgen. U installeert ook de MSAL Broker-bibliotheek die interacties met desktopverificatiebrokers afhandelt.

dotnet add package Microsoft.Identity.Client
dotnet add package Microsoft.Identity.Client.Broker

Een appsettings.json-bestand maken en registratieconfiguraties toevoegen

  1. Maak appsettings.json bestand in de hoofdmap van de app.

  2. Voeg app-registratiegegevens toe aan het appsettings.json-bestand .

    {
        "AzureAd": {
            "Authority": "https://<Enter_the_Tenant_Subdomain_Here>.ciamlogin.com/",
            "ClientId": "<Enter_the_Application_Id_Here>"
        }
    }
    
    • Vervang Enter_the_Tenant_Subdomain_Here door het subdomein Directory (tenant).
    • Vervang Enter_the_Application_Id_Here door de toepassings-id (client) van de app die u eerder hebt geregistreerd.
  3. Nadat u het app-instellingenbestand hebt gemaakt, maken we een ander bestand met de naam AzureAdConfig.cs waarmee u de configuraties uit het app-instellingenbestand kunt lezen. Maak het AzureAdConfig.cs-bestand in de hoofdmap van de app.

  4. Definieer in het bestand AzureAdConfig.js de getters en setters voor de ClientId en Authority eigenschappen. Voeg de volgende code toe:

    namespace sign_in_dotnet_wpf
    {
        public class AzureAdConfig
        {
            public string Authority { get; set; }
            public string ClientId { get; set; }
        }
    }
    

Aangepast URL-domein gebruiken (optioneel)

Gebruik een aangepast domein om de verificatie-URL volledig te merken. Vanuit gebruikersperspectief blijven gebruikers in uw domein tijdens het verificatieproces in plaats van omgeleid naar ciamlogin.com domeinnaam.

Volg deze stappen om een aangepast domein te gebruiken:

  1. Gebruik de stappen in Aangepaste URL-domeinen inschakelen voor apps in externe tenants om aangepast URL-domein in te schakelen voor uw externe tenant.

  2. Open appsettings.json bestand:

    1. Werk de waarde van de Authority eigenschap bij naar https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Here. Vervang Enter_the_Custom_Domain_Here door uw aangepaste URL-domein en Enter_the_Tenant_ID_Here door uw tenant-id. Als u uw tenant-ID niet hebt, leert u hoe u de details van uw tenant kunt lezen.
    2. Voeg knownAuthorities de eigenschap toe met een waarde [Enter_the_Custom_Domain_Here].

Nadat u de wijzigingen in uw appsettings.json-bestand hebt aangebracht, als uw aangepaste URL-domein is login.contoso.com en uw tenant-id aaaabbbb-0000-cccc-1111-dddd2222eeeee is, moet uw bestand er ongeveer uitzien als het volgende fragment:

{
    "AzureAd": {
        "Authority": "https://login.contoso.com/aaaabbbb-0000-cccc-1111-dddd2222eeee",
        "ClientId": "Enter_the_Application_Id_Here",
        "KnownAuthorities": ["login.contoso.com"]
    }
}

Het projectbestand wijzigen

  1. Navigeer naar het bestand sign-in-dotnet-wpf.csproj in de hoofdmap van de app.

  2. Voer in dit bestand de volgende twee stappen uit:

    1. Wijzig het bestand sign-in-dotnet-wpf.csproj om uw app de opdracht te geven het appsettings.json bestand naar de uitvoermap te kopiëren wanneer het project wordt gecompileerd. Voeg het volgende codefragment toe aan het bestand sign-in-dotnet-wpf.csproj :
    2. Stel het doelframework in op de build windows10.0.19041.0 om te helpen bij het lezen van token uit de tokencache, zoals te zien in de tokencache-helperklasse.
    <Project Sdk="Microsoft.NET.Sdk">
    
        ...
    
        <!-- Set target framework to target windows10.0.19041.0 build -->
        <PropertyGroup>
            <OutputType>WinExe</OutputType>
            <TargetFramework>net7.0-windows10.0.19041.0</TargetFramework> <!-- target framework -->
            <RootNamespace>sign_in_dotnet_wpf</RootNamespace>
            <Nullable>enable</Nullable>
            <UseWPF>true</UseWPF>
        </PropertyGroup>
    
        <!-- Copy appsettings.json file to output folder. -->
        <ItemGroup>
            <None Remove="appsettings.json" />
        </ItemGroup>
    
        <ItemGroup>
            <EmbeddedResource Include="appsettings.json">
                <CopyToOutputDirectory>Always</CopyToOutputDirectory>
            </EmbeddedResource>
        </ItemGroup>
    </Project>
    

Een helperklasse voor tokencache maken

Maak een helperklasse voor tokencache waarmee een tokencache wordt geïnitialiseerd. De toepassing probeert het token uit de cache te lezen voordat er een nieuw token wordt verkregen. Als het token niet in de cache wordt gevonden, verkrijgt de toepassing een nieuw token. Bij het afmelden wordt de cache gewist van alle accounts en alle bijbehorende toegangstokens.

  1. Maak een TokenCacheHelper.cs-bestand in de hoofdmap van de app.

  2. Open het TokenCacheHelper.cs bestand. Voeg de pakketten en naamruimten toe aan het bestand. In de volgende stappen vult u dit bestand in met de codelogica door de relevante logica toe te voegen aan de TokenCacheHelper klasse.

    using System.IO;
    using System.Security.Cryptography;
    using Microsoft.Identity.Client;
    
    namespace sign_in_dotnet_wpf
    {
        static class TokenCacheHelper{}
    }
    
  3. Voeg een constructor toe aan de TokenCacheHelper klasse die het pad naar het cachebestand definieert. Voor verpakte bureaublad-apps (MSIX-pakketten, ook wel desktop bridge genoemd) is de uitvoermap alleen-lezen. In dat geval moeten we Windows.Storage.ApplicationData.Current.LocalCacheFolder.Path + "\msalcache.bin" gebruiken, een lees-/schrijfmap per toepassing voor verpakte apps.

    namespace sign_in_dotnet_wpf
    {
        static class TokenCacheHelper
        {
            static TokenCacheHelper()
            {
                try
                {
                    CacheFilePath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalCacheFolder.Path, ".msalcache.bin3");
                }
                catch (System.InvalidOperationException)
                {
                    CacheFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location + ".msalcache.bin3";
                }
            }
            public static string CacheFilePath { get; private set; }
            private static readonly object FileLock = new object();
        }
    }
    
    
  4. Voeg code toe om tokencacheserialisatie te verwerken. De ITokenCache interface implementeert de openbare toegang tot cachebewerkingen. ITokenCache de interface bevat de methoden voor het abonneren op de cacheserialisatie-gebeurtenissen, terwijl de interface ITokenCacheSerializer de methoden beschikbaar maakt die u moet gebruiken in de serialisatie-gebeurtenissen van de cache om de cache te serialiseren/deserialiseren. TokenCacheNotificationArgs bevat parameters die worden gebruikt doorMicrosoft.Identity.Client (MSAL) voor toegang tot de cache. De ITokenCacheSerializer interface is beschikbaar in de TokenCacheNotificationArgs callback.

    Voeg de volgende code toe aan de klasse TokenCacheHelper:

        static class TokenCacheHelper
        {
            static TokenCacheHelper()
            {...}
            public static string CacheFilePath { get; private set; }
            private static readonly object FileLock = new object();
    
            public static void BeforeAccessNotification(TokenCacheNotificationArgs args)
            {
                lock (FileLock)
                {
                    args.TokenCache.DeserializeMsalV3(File.Exists(CacheFilePath)
                            ? ProtectedData.Unprotect(File.ReadAllBytes(CacheFilePath),
                                                     null,
                                                     DataProtectionScope.CurrentUser)
                            : null);
                }
            }
    
            public static void AfterAccessNotification(TokenCacheNotificationArgs args)
            {
                if (args.HasStateChanged)
                {
                    lock (FileLock)
                    {
                        File.WriteAllBytes(CacheFilePath,
                                           ProtectedData.Protect(args.TokenCache.SerializeMsalV3(),
                                                                 null,
                                                                 DataProtectionScope.CurrentUser)
                                          );
                    }
                }
            }
        }
    
        internal static void EnableSerialization(ITokenCache tokenCache)
        {
            tokenCache.SetBeforeAccess(BeforeAccessNotification);
            tokenCache.SetAfterAccess(AfterAccessNotification);
        }
    

    In de BeforeAccessNotification methode leest u de cache uit het bestandssysteem en als de cache niet leeg is, deserialiseert u deze en laadt u deze. De AfterAccessNotification methode wordt aangeroepen nadat Microsoft.Identity.Client (MSAL) toegang heeft tot de cache. Als de cache is gewijzigd, serialiseert u deze en houdt u de wijzigingen in de cache vast.

    De EnableSerialization bevat de ITokenCache.SetBeforeAccess() en ITokenCache.SetAfterAccess() methoden:

    • ITokenCache.SetBeforeAccess() stelt een gemachtigde in om een melding te ontvangen voordat een bibliotheekmethode toegang heeft tot de cache. Dit biedt de gedelegeerde de mogelijkheid om een cachevermelding voor de toepassing en accounts, zoals opgegeven in de TokenCacheNotificationArgs, te deserialiseren.
    • ITokenCache.SetAfterAccess() stelt een gemachtigde in om een melding te ontvangen nadat een bibliotheekmethode toegang heeft tot de cache. Dit geeft de gedelegeerde de optie om een cache-item te serialiseren voor de toepassing en accounts die zijn opgegeven in de TokenCacheNotificationArgs.

De gebruikersinterface van de WPF-bureaublad-app maken

Wijzig het bestand MainWindow.xaml om de UI-elementen voor de app toe te voegen. Open het bestand MainWindow.xaml in de hoofdmap van de app en voeg het volgende stukje code toe met de <Grid></Grid> besturingssectie.

    <StackPanel Background="Azure">
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
            <Button x:Name="SignInButton" Content="Sign-In" HorizontalAlignment="Right" Padding="5" Click="SignInButton_Click" Margin="5" FontFamily="Segoe Ui"/>
            <Button x:Name="SignOutButton" Content="Sign-Out" HorizontalAlignment="Right" Padding="5" Click="SignOutButton_Click" Margin="5" Visibility="Collapsed" FontFamily="Segoe Ui"/>
        </StackPanel>
        <Label Content="Authentication Result" Margin="0,0,0,-5" FontFamily="Segoe Ui" />
        <TextBox x:Name="ResultText" TextWrapping="Wrap" MinHeight="120" Margin="5" FontFamily="Segoe Ui"/>
        <Label Content="Token Info" Margin="0,0,0,-5" FontFamily="Segoe Ui" />
        <TextBox x:Name="TokenInfoText" TextWrapping="Wrap" MinHeight="70" Margin="5" FontFamily="Segoe Ui"/>
    </StackPanel>

Met deze code worden belangrijke UI-elementen toegevoegd. De methoden en objecten die de functionaliteit van de UI-elementen verwerken, worden gedefinieerd in het MainWindow.xaml.cs bestand dat we in de volgende stap maken.

  • Een knop waarmee de gebruiker wordt aangemeld. SignInButton_Click de methode wordt aangeroepen wanneer de gebruiker deze knop selecteert.
  • Een knop waarmee de gebruiker uitlogt. SignOutButton_Click de methode wordt aangeroepen wanneer de gebruiker deze knop selecteert.
  • Een tekstvak waarin de details van het verificatieresultaat worden weergegeven nadat de gebruiker zich heeft geprobeerd aan te melden. Informatie die hier wordt weergegeven, wordt geretourneerd door het ResultText object.
  • Een tekstvak waarin de details van het token worden weergegeven nadat de gebruiker zich heeft aangemeld. Informatie die hier wordt weergegeven, wordt geretourneerd door het TokenInfoText object.

Code toevoegen aan het MainWindow.xaml.cs-bestand

Het MainWindow.xaml.cs-bestand bevat de code die de runtimelogica biedt voor het gedrag van de UI-elementen in MainWindow.xaml.

  1. Open het bestand MainWindow.xaml.cs in de hoofdmap van de app.

  2. Voeg de volgende code toe aan het bestand om de pakketten te importeren en definieer tijdelijke aanduidingen voor de methoden die we maken.

    using Microsoft.Identity.Client;
    using System;
    using System.Linq;
    using System.Windows;
    using System.Windows.Interop;
    
    namespace sign_in_dotnet_wpf
    {
        public partial class MainWindow : Window
        {
            string[] scopes = new string[] { };
    
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private async void SignInButton_Click(object sender, RoutedEventArgs e){...}
    
            private async void SignOutButton_Click(object sender, RoutedEventArgs e){...}
    
            private void DisplayBasicTokenInfo(AuthenticationResult authResult){...}
        }
    }
    
  3. Voeg de volgende code toe aan de methode SignInButton_Click. Deze methode wordt aangeroepen wanneer de gebruiker de knop Aanmelden selecteert.

    private async void SignInButton_Click(object sender, RoutedEventArgs e)
    {
        AuthenticationResult authResult = null;
        var app = App.PublicClientApp;
    
        ResultText.Text = string.Empty;
        TokenInfoText.Text = string.Empty;
    
        IAccount firstAccount;
    
        var accounts = await app.GetAccountsAsync();
        firstAccount = accounts.FirstOrDefault();
    
        try
        {
            authResult = await app.AcquireTokenSilent(scopes, firstAccount)
                    .ExecuteAsync();
        }
        catch (MsalUiRequiredException ex)
        {
            try
            {
                authResult = await app.AcquireTokenInteractive(scopes)
                    .WithAccount(firstAccount)
                    .WithParentActivityOrWindow(new WindowInteropHelper(this).Handle) 
                    .WithPrompt(Prompt.SelectAccount)
                    .ExecuteAsync();
            }
            catch (MsalException msalex)
            {
                ResultText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
            }
            catch (Exception ex)
            {
                ResultText.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
                return;
            }
    
            if (authResult != null)
            {
                ResultText.Text = "Sign in was successful.";
                DisplayBasicTokenInfo(authResult);
                this.SignInButton.Visibility = Visibility.Collapsed;
                this.SignOutButton.Visibility = Visibility.Visible;
            }
        }
    }
    

    GetAccountsAsync() retourneert alle beschikbare accounts in de gebruikerstokencache voor de app. De IAccount-interface vertegenwoordigt informatie over één account.

    Om tokens te verkrijgen, probeert de app het token op de achtergrond te verkrijgen met behulp van de AcquireTokenSilent methode om te controleren of een acceptabel token zich in de cache bevindt. De AcquireTokenSilent methode kan bijvoorbeeld mislukken omdat de gebruiker zich heeft afgemeld. Wanneer MSAL detecteert dat het probleem kan worden opgelost door een interactieve actie te vereisen, wordt er een MsalUiRequiredException uitzondering gegenereerd. Deze uitzondering zorgt ervoor dat de app interactief een token verkrijgt.

    De methode AcquireTokenInteractive aanroepen, resulteert in een venster waarin de gebruiker wordt gevraagd zich aan te melden. Voor apps moeten gebruikers zich meestal interactief aanmelden wanneer ze zich voor het eerst moeten verifiëren. Ze moeten zich mogelijk ook aanmelden wanneer er een stil proces is om een token te verkrijgen. Nadat AcquireTokenInteractive voor de eerste keer is uitgevoerd, wordt AcquireTokenSilent de gebruikelijke methode om tokens te verkrijgen.

  4. Voeg de volgende code toe aan de methode SignOutButton_Click. Deze methode wordt aangeroepen wanneer de gebruiker de knop Afmelden selecteert.

    private async void SignOutButton_Click(object sender, RoutedEventArgs e)
    {
        var accounts = await App.PublicClientApp.GetAccountsAsync();
        if (accounts.Any())
        {
            try
            {
                await App.PublicClientApp.RemoveAsync(accounts.FirstOrDefault());
                this.ResultText.Text = "User has signed-out";
                this.TokenInfoText.Text = string.Empty;
                this.SignInButton.Visibility = Visibility.Visible;
                this.SignOutButton.Visibility = Visibility.Collapsed;
            }
            catch (MsalException ex)
            {
                ResultText.Text = $"Error signing-out user: {ex.Message}";
            }
        }
    }
    

    De SignOutButton_Click-methode wist de cache voor alle accounts en de bijbehorende toegangstokens. De volgende keer dat de gebruiker zich probeert aan te melden, moet hij of zij dit interactief doen.

  5. Voeg de volgende code toe aan de methode DisplayBasicTokenInfo. Met deze methode wordt basisinformatie over het token weergegeven.

    private void DisplayBasicTokenInfo(AuthenticationResult authResult)
    {
        TokenInfoText.Text = "";
        if (authResult != null)
        {
            TokenInfoText.Text += $"Username: {authResult.Account.Username}" + Environment.NewLine;
            TokenInfoText.Text += $"{authResult.Account.HomeAccountId}" + Environment.NewLine;
        }
    }
    

Code toevoegen aan het App.xaml.cs-bestand

App.xaml is waar u resources declareert die in de app worden gebruikt. Het is het toegangspunt voor uw app. App.xaml.cs is de code achter het bestand voor App.xaml. App.xaml.cs definieert ook het startvenster voor uw toepassing.

Open het bestand App.xaml.cs in de hoofdmap van de app en voeg de volgende code eraan toe.

using System.Windows;
using System.Reflection;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Broker;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;

namespace sign_in_dotnet_wpf
{
    public partial class App : Application
    {
        static App()
        {
            CreateApplication();
        }

        public static void CreateApplication()
        {
            var assembly = Assembly.GetExecutingAssembly();
            using var stream = assembly.GetManifestResourceStream("sign_in_dotnet_wpf.appsettings.json");
            AppConfiguration = new ConfigurationBuilder()
                .AddJsonStream(stream)
                .Build();

            AzureAdConfig azureADConfig = AppConfiguration.GetSection("AzureAd").Get<AzureAdConfig>();

            var builder = PublicClientApplicationBuilder.Create(azureADConfig.ClientId)
                .WithAuthority(azureADConfig.Authority)
                .WithDefaultRedirectUri();

            _clientApp = builder.Build();
            TokenCacheHelper.EnableSerialization(_clientApp.UserTokenCache);
        }

        private static IPublicClientApplication _clientApp;
        private static IConfiguration AppConfiguration;
        public static IPublicClientApplication PublicClientApp { get { return _clientApp; } }
    }
}

In deze stap laadt u het appsettings.json-bestand . Met de opbouwfunctie voor configuraties kunt u de app-configuraties lezen die zijn gedefinieerd in het appsettings.json-bestand . U definieert de WPF-app ook als een openbare client-app omdat het een desktop-app is. De TokenCacheHelper.EnableSerialization methode maakt serialisatie van de tokencache mogelijk.

De app uitvoeren

Voer uw app uit en meld u aan om de toepassing te testen

  1. Navigeer in uw terminal naar de hoofdmap van uw WPF-app en voer de app uit door de opdracht dotnet run uit te voeren in uw terminal.

  2. Nadat u het voorbeeld hebt gestart, ziet u een venster met een aanmeldingsknop . Selecteer de knop Aanmelden.

    Schermopname van het aanmeldingsscherm voor een WPF-bureaubladtoepassing.

  3. Voer op de aanmeldingspagina uw e-mailadres van uw account in. Als u geen account hebt, selecteert u Geen account? Maak er een, waarmee de registratiestroom wordt gestart. Volg deze stroom om een nieuw account te maken en u aan te melden.

  4. Zodra u zich aanmeldt, ziet u een scherm met geslaagde aanmelding en basisinformatie over uw gebruikersaccount dat is opgeslagen in het opgehaalde token. De basisinformatie wordt weergegeven in de sectie Tokengegevens van het aanmeldingsscherm

Zie ook