Bagikan melalui


Tutorial: Mengautentikasi pengguna ke aplikasi desktop WPF Anda

Berlaku untuk: Lingkaran putih dengan simbol X abu-abu. Penyewa tenaga kerja Lingkaran hijau dengan simbol tanda centang putih. Penyewa eksternal (lihat selengkapnya)

Tutorial ini menunjukkan cara membuat aplikasi desktop Windows Presentation Form (WPF) dan menyiapkannya untuk autentikasi menggunakan pusat admin Microsoft Entra.

Di tutorial ini, Anda akan:

  • Konfigurasikan aplikasi desktop WPF untuk menggunakan rincian pendaftaran aplikasi.
  • Buat aplikasi desktop yang mendaftarkan pengguna dan memperoleh token atas nama pengguna.

Prasyarat

Membuat aplikasi desktop WPF

  1. Buka terminal Anda dan navigasikan ke folder tempat Anda ingin proyek Anda tinggal.

  2. Menginisialisasi aplikasi desktop WPF dan menavigasi ke folder akarnya.

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

Memasang paket

Instal penyedia konfigurasi yang membantu aplikasi kami membaca data konfigurasi dari pasangan kunci-nilai dalam file pengaturan aplikasi kami. Abstraksi konfigurasi ini memberikan kemampuan untuk mengikat nilai konfigurasi ke instans objek .NET.

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

Instal Microsoft Authentication Library (MSAL) yang berisi semua komponen utama yang Anda butuhkan untuk memperoleh token. Anda juga menginstal perpustakaan broker MSAL yang menangani interaksi dengan broker autentikasi desktop.

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

Membuat file appsettings.json dan menambahkan konfigurasi pendaftaran

  1. Buat file appsettings.json di folder akar aplikasi.

  2. Tambahkan detail pendaftaran aplikasi ke file appsettings.json .

    {
        "AzureAd": {
            "Authority": "https://<Enter_the_Tenant_Subdomain_Here>.ciamlogin.com/",
            "ClientId": "<Enter_the_Application_Id_Here>"
        }
    }
    
    • Ganti Enter_the_Tenant_Subdomain_Here dengan subdomain Direktori (penyewa).
    • Ganti Enter_the_Application_Id_Here dengan ID Aplikasi (klien) aplikasi yang Anda daftarkan sebelumnya.
  3. Setelah membuat file pengaturan aplikasi, kami akan membuat file lain yang disebut AzureAdConfig.cs yang akan membantu Anda membaca konfigurasi dari file pengaturan aplikasi. Buat file AzureAdConfig.cs di folder akar aplikasi.

  4. Dalam file AzureAdConfig.js, tentukan getter dan setter untuk properti ClientId dan Authority. Tambahkan kode berikut:

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

Menggunakan domain URL kustom (Opsional)

Gunakan domain kustom untuk sepenuhnya memberi merek URL autentikasi. Dari perspektif pengguna, pengguna tetap berada di domain Anda selama proses autentikasi, daripada dialihkan ke nama domain ciamlogin.com.

Ikuti langkah-langkah ini untuk menggunakan domain kustom:

  1. Gunakan langkah-langkah dalam Mengaktifkan domain URL kustom untuk aplikasi di penyewa eksternal untuk mengaktifkan domain URL kustom untuk penyewa eksternal Anda.

  2. Buka file appsettings.json:

    1. Perbarui nilai properti Authority ke https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Here. Ganti Enter_the_Custom_Domain_Here dengan domain URL kustom Anda dan Enter_the_Tenant_ID_Here dengan ID penyewa Anda. Jika Anda tidak memiliki ID penyewa, pelajari cara membaca rincian penyewa Anda.
    2. Tambahkan properti knownAuthorities dengan nilai [Enter_the_Custom_Domain_Here].

Setelah Anda membuat perubahan pada file appsettings.json Anda, jika domain URL kustom Anda login.contoso.com, dan ID penyewa Anda aaaabbbb-0000-cc-1111-dddd22222ee, maka file Anda akan terlihat mirip dengan cuplikan berikut:

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

Mengubah file proyek

  1. Navigasikan ke file sign-in-dotnet-wpf.csproj di folder akar aplikasi.

  2. Dalam file ini, lakukan dua langkah berikut:

    1. Ubah file sign-in-dotnet-wpf.csproj untuk menginstruksikan aplikasi Anda menyalin file appsettings.json ke direktori output saat proyek dikompilasi. Tambahkan bagian kode berikut ke file sign-in-dotnet-wpf.csproj :
    2. Atur kerangka kerja target menjadi build windows10.0.19041.0 untuk membantu membaca token yang di-cache seperti yang terlihat di kelas pembantu cache token.
    <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>
    

Membuat kelas pembantu cache token

Buat kelas pembantu cache token yang menginisialisasi cache token. Aplikasi mencoba membaca token dari cache sebelum mencoba memperoleh token baru. Jika token tidak ditemukan di cache, aplikasi memperoleh token baru. Setelah keluar, cache dihapus dari semua akun dan semua token akses yang sesuai.

  1. Buat file TokenCacheHelper.cs di folder akar aplikasi.

  2. Buka file TokenCacheHelper.cs. Tambahkan paket dan namespace ke file. Dalam langkah-langkah berikut, Anda mengisi file ini dengan logika kode dengan menambahkan logika yang relevan ke TokenCacheHelper kelas .

    using System.IO;
    using System.Security.Cryptography;
    using Microsoft.Identity.Client;
    
    namespace sign_in_dotnet_wpf
    {
        static class TokenCacheHelper{}
    }
    
  3. Tambahkan konstruktor ke TokenCacheHelper kelas yang menentukan jalur file cache. Untuk aplikasi desktop yang dipaketkan (paket MSIX, juga disebut Desktop Bridge), folder perakitan yang dijalankan bersifat baca-saja. Dalam hal ini kita perlu menggunakan Windows.Storage.ApplicationData.Current.LocalCacheFolder.Path + "\msalcache.bin" yang merupakan folder baca/tulis per aplikasi untuk aplikasi yang dipaketkan.

    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. Tambahkan kode untuk menangani serialisasi cache token. Antarmuka ITokenCache mengimplementasikan akses publik ke operasi cache. ITokenCache antarmuka berisi metode untuk berlangganan peristiwa serialisasi cache, sementara antarmuka ITokenCacheSerializer mengekspos metode yang perlu Anda gunakan dalam peristiwa serialisasi cache, untuk membuat serialisasi/deserialisasi cache. TokenCacheNotificationArgs berisi parameter yang digunakan dalam panggilan Microsoft.Identity.Client (MSAL) untuk mengakses cache. ITokenCacheSerializer antarmuka dapat digunakan di TokenCacheNotificationArgs panggilan balik.

    Tambahkan kode berikut ke kelas 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);
        }
    

    Dalam metode ini BeforeAccessNotification, Anda membaca cache dari sistem file, dan jika cache tersebut tidak kosong, Anda mendeserialisasinya dan memuatnya. Metode AfterAccessNotification ini dipanggil setelah Microsoft.Identity.Client (MSAL) mengakses cache. Jika cache telah berubah, Anda menserialisasikannya dan mempertahankan perubahan pada cache.

    EnableSerialization berisi metode ITokenCache.SetBeforeAccess() dan ITokenCache.SetAfterAccess().

    • ITokenCache.SetBeforeAccess() mengatur wakil yang akan menerima pemberitahuan sebelum metode pustaka mengakses cache. Ini memberikan pilihan kepada delegasi untuk mendeserialisasi entri cache untuk aplikasi dan akun-akun yang ditentukan dalam tag TokenCacheNotificationArgs.
    • ITokenCache.SetAfterAccess() mengatur delegat yang akan diberi tahu setelah fungsi pustaka mengakses cache. Ini memberikan opsi kepada delegasi untuk membuat serialisasi entri cache untuk aplikasi dan akun yang ditentukan dalam TokenCacheNotificationArgs.

Membuat UI aplikasi desktop WPF

Ubah file MainWindow.xaml untuk menambahkan elemen UI untuk aplikasi. Buka file MainWindow.xaml di folder akar aplikasi dan tambahkan bagian kode berikut dengan bagian <Grid></Grid> kontrol.

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

Kode ini menambahkan elemen UI kunci. Metode dan objek yang menangani fungsionalitas elemen UI didefinisikan dalam file MainWindow.xaml.cs yang kita buat di langkah berikutnya.

  • Tombol untuk masuk pengguna. SignInButton_Click metode dipanggil ketika pengguna memilih tombol ini.
  • Tombol yang mengeluarkan pengguna dari akun. SignOutButton_Click metode dipanggil ketika pengguna memilih tombol ini.
  • Kotak teks yang menampilkan detail hasil autentikasi setelah pengguna mencoba masuk. Informasi yang ditampilkan di sini dikembalikan oleh ResultText objek.
  • Kotak teks yang menampilkan detail token setelah pengguna berhasil masuk. Informasi yang ditampilkan di sini dikembalikan oleh TokenInfoText objek.

Menambahkan kode ke file MainWindow.xaml.cs

File MainWindow.xaml.cs berisi kode yang menyediakan logika runtime th untuk perilaku elemen UI dalam file MainWindow.xaml .

  1. Buka file MainWindow.xaml.cs di folder akar aplikasi.

  2. Tambahkan kode berikut ke dalam file untuk mengimpor paket, dan tentukan placeholder untuk metode yang kita buat.

    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. Tambahkan kode berikut ke metode SignInButton_Click. Metode ini dipanggil ketika pengguna memilih tombol Masuk .

    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() mengembalikan semua akun yang tersedia di cache token pengguna untuk aplikasi. Antarmuka IAccount mewakili informasi tentang satu akun.

    Untuk memperoleh token, aplikasi mencoba memperoleh token secara diam-diam AcquireTokenSilent menggunakan metode untuk memverifikasi apakah token yang dapat diterima ada di cache. Metode AcquireTokenSilent mungkin gagal, misalnya, karena pengguna keluar. Ketika MSAL mendeteksi bahwa masalah dapat diselesaikan dengan memerlukan tindakan interaktif, MSAL melemparkan MsalUiRequiredException pengecualian. Pengecualian ini menyebabkan aplikasi memperoleh token secara interaktif.

    Memanggil metode AcquireTokenInteractive akan menampilkan jendela yang meminta pengguna untuk masuk. Aplikasi biasanya mengharuskan pengguna untuk masuk secara interaktif saat pertama kali mereka perlu mengautentikasi. Mereka mungkin juga perlu masuk saat proses tanpa suara untuk memperoleh token. Setelah AcquireTokenInteractive dijalankan untuk pertama kalinya, AcquireTokenSilent menjadi metode yang biasa digunakan untuk mendapatkan token

  4. Tambahkan kode berikut ke metode SignOutButton_Click. Metode ini dipanggil ketika pengguna memilih tombol Keluar .

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

    Metode ini SignOutButton_Click menghapus cache semua akun dan semua token akses yang sesuai. Lain kali pengguna mencoba masuk, mereka harus melakukannya secara interaktif.

  5. Tambahkan kode berikut ke metode DisplayBasicTokenInfo. Metode ini menampilkan informasi dasar tentang token.

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

Menambahkan kode ke file App.xaml.cs

App.xaml adalah tempat Anda mendeklarasikan sumber daya yang digunakan di seluruh aplikasi. Ini adalah titik masuk untuk aplikasi Anda. App.xaml.cs adalah kode di belakang file untuk App.xaml. App.xaml.cs juga menentukan jendela mulai untuk aplikasi Anda.

Buka file App.xaml.cs di folder akar aplikasi, lalu tambahkan kode berikut ke dalamnya.

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

Dalam langkah ini, Anda memuat file appsettings.json . Penyusun konfigurasi membantu Anda membaca konfigurasi aplikasi yang ditentukan dalam file appsettings.json . Anda juga menentukan aplikasi WPF sebagai aplikasi klien publik karena ini adalah aplikasi desktop. Metode ini TokenCacheHelper.EnableSerialization memungkinkan serialisasi cache token.

Menjalankan aplikasi

Jalankan aplikasi Anda dan masuk untuk menguji aplikasi

  1. Di terminal Anda, navigasikan ke folder akar aplikasi WPF Anda dan jalankan aplikasi dengan menjalankan perintah dotnet run di terminal Anda.

  2. Setelah meluncurkan sampel, Anda akan melihat jendela dengan tombol Masuk . Pilih tombol Masuk .

    Cuplikan layar masuk untuk aplikasi desktop WPF.

  3. Pada halaman masuk, masukkan alamat email akun Anda. Jika Anda tidak memiliki akun, pilih Tidak ada akun? Buat, yang memulai alur pendaftaran. Ikuti alur ini untuk membuat akun baru dan masuk.

  4. Setelah masuk, Anda akan melihat layar yang menampilkan keberhasilan masuk dan informasi dasar tentang akun pengguna Anda yang disimpan dalam token yang diambil. Informasi dasar ditampilkan di bagian Info Token layar login

Lihat juga