Udostępnij za pośrednictwem


Samouczek: uwierzytelnij użytkowników w swojej aplikacji pulpitowej WPF

Dotyczy:Biały okrąg z szarym symbolem X. Najemcy pracowniczy Zielony okrąg z białym symbolem haczyka. Najemcy zewnętrzni (dowiedz się więcej)

W tym samouczku pokazano, jak utworzyć aplikację klasyczną Windows Presentation Form (WPF) i przygotować ją do uwierzytelniania przy użyciu centrum administracyjnego firmy Microsoft Entra.

W tym samouczku nauczysz się następujących rzeczy:

  • Skonfiguruj aplikację klasyczną WPF, aby korzystała ze szczegółów rejestracji aplikacji.
  • Utwórz aplikację desktopową, która loguje użytkownika i uzyskuje token w jego imieniu.

Wymagania wstępne

Stwórz aplikację WPF na komputer stacjonarny

  1. Otwórz terminal i przejdź do folderu, w którym ma działać projekt.

  2. Zainicjuj aplikację klasyczną WPF i przejdź do folderu głównego.

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

Instalowanie pakietów

Zainstaluj dostawców konfiguracji, którzy pomagają naszej aplikacji odczytywać dane konfiguracji z par klucz-wartość w pliku ustawień aplikacji. Te abstrakcje konfiguracji umożliwiają powiązanie wartości konfiguracji z wystąpieniami obiektów platformy .NET.

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

Zainstaluj bibliotekę Microsoft Authentication Library (MSAL), która zawiera wszystkie kluczowe składniki potrzebne do uzyskania tokenu. Należy również zainstalować bibliotekę brokera MSAL, która obsługuje interakcje z brokerami uwierzytelniania pulpitu.

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

Tworzenie pliku appsettings.json i dodawanie konfiguracji rejestracji

  1. Utwórz plik appsettings.json w folderze głównym aplikacji.

  2. Dodaj szczegóły rejestracji aplikacji do pliku appsettings.json .

    {
        "AzureAd": {
            "Authority": "https://<Enter_the_Tenant_Subdomain_Here>.ciamlogin.com/",
            "ClientId": "<Enter_the_Application_Id_Here>"
        }
    }
    
    • Zastąp Enter_the_Tenant_Subdomain_Here poddomeną katalogu dla dzierżawcy.
    • Zastąp Enter_the_Application_Id_Here identyfikatorem aplikacji (klienta), którą zarejestrowałeś wcześniej.
  3. Po utworzeniu pliku ustawień aplikacji utworzymy inny plik o nazwie AzureAdConfig.cs , który pomoże Ci odczytać konfiguracje z pliku ustawień aplikacji. Utwórz plik AzureAdConfig.cs w folderze głównym aplikacji.

  4. W pliku AzureAdConfig.js zdefiniuj metody getters i setters dla właściwości ClientId i Authority. Dodaj następujący kod:

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

Użyj niestandardowej domeny adresu URL (opcjonalnie)

Użyj domeny niestandardowej, aby w pełni oznaczyć adres URL uwierzytelniania. Z perspektywy użytkownika użytkownicy pozostają na twojej domenie podczas procesu uwierzytelniania, a nie są przekierowywani na domenę ciamlogin.com.

Wykonaj następujące kroki, aby użyć domeny niestandardowej:

  1. Aby włączyć niestandardową domenę URL dla twojej dzierżawy zewnętrznej, wykonaj kroki opisane w temacie Włączanie niestandardowych domen URL dla aplikacji w dzierżawach zewnętrznych.

  2. Otwórz plik appsettings.json:

    1. Zaktualizuj wartość Authority właściwości na https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Here. Zastąp Enter_the_Custom_Domain_Here swoją własną domeną URL i Enter_the_Tenant_ID_Here swoim identyfikatorem najemcy. Jeśli nie masz identyfikatora dzierżawy, dowiedz się, jak odczytywać szczegóły dzierżawy.
    2. Dodaj knownAuthorities właściwość o wartości [Enter_the_Custom_Domain_Here].

Po wprowadzeniu zmian w pliku appsettings.json, jeśli Twoja niestandardowa domena URL to login.contoso.com, a identyfikator dzierżawy to aaaabbbb-0000-cccc-1111-dddd2222eeee, plik powinien wyglądać podobnie do następującego fragmentu kodu:

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

Modyfikowanie pliku projektu

  1. Przejdź do pliku sign-in-dotnet-wpf.csproj w folderze głównym aplikacji.

  2. W tym pliku wykonaj następujące dwa kroki:

    1. Zmodyfikuj plik sign-in-dotnet-wpf.csproj , aby polecił aplikacji skopiowanie pliku appsettings.json do katalogu wyjściowego podczas kompilowania projektu. Dodaj następujący fragment kodu do pliku sign-in-dotnet-wpf.csproj :
    2. Ustaw platformę docelową na kompilację systemu Windows10.0.19041.0, aby ułatwić odczytywanie buforowanego tokenu z pamięci podręcznej tokenu, jak widać w klasie pomocniczej pamięci podręcznej tokenów.
    <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>
    

Utwórz klasę pomocniczą pamięci podręcznej tokenu

Utwórz pomocniczą klasę pamięci podręcznej tokenów, która inicjuje pamięć podręczną tokenów. Aplikacja próbuje odczytać token z pamięci podręcznej, zanim podejmie próbę uzyskania nowego tokenu. Jeśli token nie zostanie znaleziony w pamięci podręcznej, aplikacja uzyska nowy token. Po wylogowaniu pamięć podręczna jest usuwana ze wszystkich kont i odpowiednich tokenów dostępu.

  1. Utwórz plik TokenCacheHelper.cs w folderze głównym aplikacji.

  2. Otwórz plik TokenCacheHelper.cs. Dodaj pakiety i przestrzenie nazw do pliku. W poniższych krokach należy wypełnić ten plik logiką kodu, dodając odpowiednią logikę TokenCacheHelper do klasy.

    using System.IO;
    using System.Security.Cryptography;
    using Microsoft.Identity.Client;
    
    namespace sign_in_dotnet_wpf
    {
        static class TokenCacheHelper{}
    }
    
  3. Dodaj konstruktora do TokenCacheHelper klasy, która definiuje ścieżkę pliku pamięci podręcznej. W przypadku spakowanych aplikacji klasycznych (pakietów MSIX, nazywanych również Desktop Bridge) folder wykonywanej biblioteki jest tylko do odczytu. W takim przypadku musimy użyć Windows.Storage.ApplicationData.Current.LocalCacheFolder.Path + "\msalcache.bin", który jest folderem do odczytu/zapisu dla aplikacji spakowanych.

    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. Dodaj kod do obsługi serializacji pamięci podręcznej tokenu. Interfejs ITokenCache implementuje publiczny dostęp do operacji pamięci podręcznej. ITokenCache interfejs zawiera metody subskrybowania zdarzeń serializacji pamięci podręcznej, podczas gdy interfejs ITokenCacheSerializer uwidacznia metody, których należy użyć w zdarzeniach serializacji pamięci podręcznej, w celu serializacji/deserializacji pamięci podręcznej. TokenCacheNotificationArgs zawiera parametry używane przezMicrosoft.Identity.Client wywołanie MSAL , które uzyskuje dostęp do pamięci podręcznej. ITokenCacheSerializer interfejs jest dostępny w TokenCacheNotificationArgs wywołaniu zwrotnym.

    Dodaj następujący kod do TokenCacheHelper klasy:

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

    W metodzie BeforeAccessNotification odczytasz pamięć podręczną z systemu plików, a jeśli pamięć podręczna nie jest pusta, zdeserializujesz ją i załadujesz. Metoda AfterAccessNotification jest wywoływana po uzyskaniu dostępu do cache przez bibliotekę MSAL Microsoft.Identity.Client. Jeśli pamięć podręczna uległa zmianie, należy ją serializować i utrwalać zmiany w pamięci podręcznej.

    Element EnableSerialization zawiera metody ITokenCache.SetBeforeAccess() oraz ITokenCache.SetAfterAccess():

    • ITokenCache.SetBeforeAccess() Ustawia pełnomocnika, który ma być powiadamiany, zanim jakakolwiek metoda biblioteki uzyska dostęp do pamięci podręcznej. To daje opcję dla delegata do deserializacji wpisu pamięci podręcznej dla aplikacji i kont wskazanych w pliku TokenCacheNotificationArgs.
    • ITokenCache.SetAfterAccess() Ustawia delegata, który ma być powiadamiany po uzyskaniu dostępu do pamięci podręcznej przez dowolną metodę biblioteki. To przekazuje delegatowi możliwość zserializowania wpisu pamięci podręcznej dla aplikacji i kont określonych w TokenCacheNotificationArgs.

Tworzenie interfejsu użytkownika aplikacji desktopowej WPF

Zmodyfikuj plik MainWindow.xaml , aby dodać elementy interfejsu użytkownika dla aplikacji. Otwórz plik MainWindow.xaml w folderze głównym aplikacji i dodaj następujący fragment kodu z sekcją kontrolki<Grid></Grid>.

    <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>

Ten kod dodaje kluczowe elementy interfejsu użytkownika. Metody i obiekty obsługujące funkcje elementów interfejsu użytkownika są zdefiniowane w pliku MainWindow.xaml.cs tworzonym w następnym kroku.

  • Przycisk, który loguje użytkownika. SignInButton_Click metoda jest wywoływana, gdy użytkownik wybierze ten przycisk.
  • Przycisk, który powoduje wylogowanie użytkownika. SignOutButton_Click metoda jest wywoływana, gdy użytkownik wybierze ten przycisk.
  • Pole tekstowe, które wyświetla szczegóły wyniku uwierzytelniania po próbie zalogowania się przez użytkownika. Wyświetlane tutaj informacje są zwracane przez ResultText obiekt.
  • Pole tekstowe, które wyświetla szczegóły tokenu po pomyślnym zalogowaniu użytkownika. Wyświetlane tutaj informacje są zwracane przez TokenInfoText obiekt.

Dodawanie kodu do pliku MainWindow.xaml.cs

Plik MainWindow.xaml.cs zawiera kod, który zapewnia logikę wykonywania dla zachowania elementów interfejsu, które znajdują się w pliku MainWindow.xaml.

  1. Otwórz plik MainWindow.xaml.cs w folderze głównym aplikacji.

  2. Dodaj następujący kod w pliku w celu zaimportowania pakietów i zdefiniuj symbole zastępcze dla tworzonych metod.

    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. Dodaj następujący kod do metody SignInButton_Click: Ta metoda jest wywoływana, gdy użytkownik wybierze przycisk Zaloguj się .

    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() Zwraca wszystkie dostępne konta w pamięci podręcznej tokenu użytkownika dla aplikacji. Interfejs IAccount reprezentuje informacje o pojedynczym koncie.

    Aby uzyskać tokeny, aplikacja próbuje uzyskać token dyskretnie przy użyciu AcquireTokenSilent metody w celu sprawdzenia, czy akceptowalny token znajduje się w pamięci podręcznej. Metoda AcquireTokenSilent może zakończyć się niepowodzeniem, na przykład dlatego, że użytkownik wylogował się. Gdy biblioteka MSAL wykryje, że problem można rozwiązać, wymagając interaktywnej akcji, zgłasza wyjątek MsalUiRequiredException . Ten wyjątek powoduje, że aplikacja uzyskuje token interaktywnie.

    AcquireTokenInteractive Wywołanie metody powoduje wyświetlenie okna z monitem o zalogowanie użytkowników. Aplikacje zwykle wymagają, aby użytkownicy logowali się interaktywnie przy pierwszym uwierzytelnieniu. Konieczne może być także zalogowanie się podczas cichej operacji uzyskania tokenu. Po tym jak AcquireTokenInteractive zostanie wykonane po raz pierwszy, AcquireTokenSilent staje się standardową metodą uzyskiwania tokenów.

  4. Dodaj następujący kod do metody SignOutButton_Click: Ta metoda jest wywoływana, gdy użytkownik wybierze przycisk Wyloguj.

    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}";
            }
        }
    }
    

    Metoda SignOutButton_Click usuwa pamięć podręczną wszystkich kont i wszystkich odpowiednich tokenów dostępu. Następnym razem, gdy użytkownik spróbuje się zalogować, będzie musiał to zrobić interaktywnie.

  5. Dodaj następujący kod do metody DisplayBasicTokenInfo: Ta metoda wyświetla podstawowe informacje o tokenie.

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

Dodawanie kodu do pliku App.xaml.cs

App.xaml to miejsce, w którym deklarujesz zasoby używane w całej aplikacji. Jest to punkt wejścia aplikacji. App.xaml.cs jest kodem stojącym za plikiem app.xaml. App.xaml.cs również definiuje okno uruchamiania aplikacji.

Otwórz plik App.xaml.cs w folderze głównym aplikacji, a następnie dodaj do niego następujący kod.

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

W tym kroku załadujesz plik appsettings.json . Konstruktor konfiguracji ułatwia odczytywanie konfiguracji aplikacji zdefiniowanych w pliku appsettings.json . Aplikację WPF można również zdefiniować jako publiczną aplikację kliencką, ponieważ jest to aplikacja desktopowa. Metoda TokenCacheHelper.EnableSerialization umożliwia serializację pamięci podręcznej tokenów.

Uruchom aplikację

Uruchamianie aplikacji i logowanie się w celu przetestowania aplikacji

  1. W terminalu przejdź do folderu głównego aplikacji WPF i uruchom aplikację, uruchamiając polecenie dotnet run w terminalu.

  2. Po uruchomieniu przykładu powinno zostać wyświetlone okno z przyciskiem Zaloguj się. Wybierz przycisk Zaloguj się.

    Zrzut ekranu przedstawiający ekran logowania dla aplikacji klasycznej WPF.

  3. Na stronie logowania wprowadź swój adres e-mail konta. Jeśli nie masz konta, wybierz pozycję Nie masz konta? Załóż konto, co uruchamia proces rejestracji. Wykonaj czynności opisane w tym przepływie, aby utworzyć nowe konto i zalogować się.

  4. Po zalogowaniu zobaczysz ekran pokazujący pomyślne logowanie oraz podstawowe informacje o koncie użytkownika przechowywane w pobranym tokenie. Podstawowe informacje są wyświetlane w sekcji Informacje o tokenie na ekranie logowania

Zobacz także