Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Si applica a:
Tenant esterni (altre informazioni)
Questa esercitazione illustra come creare un'app desktop Windows Presentation Form (WPF) e prepararla per l'autenticazione usando l'interfaccia di amministrazione di Microsoft Entra.
In questa esercitazione, farai:
- Configurare un'app desktop WPF per utilizzare i dettagli di registrazione dell'app.
- Creare un'app desktop che effettua l'accesso di un utente e acquisisce un token per conto dell'utente.
Prerequisiti
- Registrare una nuova app nell'interfaccia di amministrazione di Microsoft Entra configurata per gli account in qualsiasi directory organizzativa e account Microsoft personali. Per altri dettagli, vedere Registrare un'applicazione . Registrare i valori seguenti dalla pagina Panoramica dell'applicazione per usarli in un secondo momento:
- ID applicazione (cliente)
- ID della directory (cliente)
- Nome di dominio della directory (tenant), ad esempio contoso.onmicrosoft.com o contoso.com.
- Aggiungi gli URI di reindirizzamento seguenti utilizzando la configurazione della piattaforma delle applicazioni mobili e per desktop. Per altri dettagli, vedere Come aggiungere un URI di reindirizzamento nell'applicazione .
-
URI di reindirizzamento:
https://login.microsoftonline.com/common/oauth2/nativeclient
-
URI di reindirizzamento:
- Associare l'app a un flusso utente nell'interfaccia di amministrazione di Microsoft Entra. Questo flusso utente può essere usato in più applicazioni. Per ulteriori informazioni, vedere Creare flussi di registrazione self-service degli utenti per le app nei tenant esterni e Aggiungere la propria applicazione al flusso di utenti.
- .NET 7.0 SDK o versioni successive.
- Anche se è possibile usare qualsiasi ambiente di sviluppo integrato (IDE) che supporti le applicazioni React, questa esercitazione usa Visual Studio Code.
Creare un'applicazione desktop WPF
Aprire il terminale e passare alla cartella in cui si desidera che il progetto sia attivo.
Inizializzare un'app desktop WPF e passare alla relativa cartella radice.
dotnet new wpf --language "C#" --name sign-in-dotnet-wpf cd sign-in-dotnet-wpf
Installare i pacchetti
Installare provider di configurazione che permettano all'app la lettura dei dati di configurazione dalle coppie chiave-valore nel file delle impostazioni dell'app. Queste astrazioni di configurazione consentono di associare valori di configurazione a istanze di oggetti .NET.
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Configuration.Binder
Installare Microsoft Authentication Library (MSAL) che contiene tutti i componenti chiave necessari per acquisire un token. È necessario installare anche la libreria broker MSAL che gestisce le interazioni con i broker di autenticazione desktop.
dotnet add package Microsoft.Identity.Client
dotnet add package Microsoft.Identity.Client.Broker
Creare un file appsettings.json e aggiungere configurazioni di registrazione
Creare il file appsettings.json nella cartella radice dell'app.
Aggiungere i dettagli di registrazione dell'app al file appsettings.json.
{ "AzureAd": { "Authority": "https://<Enter_the_Tenant_Subdomain_Here>.ciamlogin.com/", "ClientId": "<Enter_the_Application_Id_Here>" } }- Sostituire
Enter_the_Tenant_Subdomain_Herecon il sottodominio Directory (tenant). - Sostituire
Enter_the_Application_Id_Herecon l'ID applicazione (client) dell’app registrata in precedenza.
- Sostituire
Dopo aver creato il file delle impostazioni dell'app, verrà creato un altro file denominato AzureAdConfig.cs che consentirà di leggere le configurazioni dal file delle impostazioni dell'app. Creare il file AzureAdConfig.cs nella cartella radice dell'app.
Nel file AzureAdConfig.js definire i getter e i setter per le proprietà
ClientIdeAuthority. Aggiungere il codice seguente:namespace sign_in_dotnet_wpf { public class AzureAdConfig { public string Authority { get; set; } public string ClientId { get; set; } } }
Usare un dominio URL personalizzato (facoltativo)
Usare un dominio personalizzato per personalizzare completamente l'URL di autenticazione. Dal punto di vista dell'utente, gli utenti rimangono sul tuo dominio durante il processo di autenticazione, anziché essere reindirizzati al nome di dominio ciamlogin.com.
Seguire questa procedura per usare un dominio personalizzato:
Usare la procedura descritta in Abilitare domini URL personalizzati per le app nei tenant esterni per abilitare il dominio URL personalizzato per il tenant esterno.
Aprire il file appsettings.json.
- Aggiornare il valore della proprietà da
Authoritya https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Here. SostituireEnter_the_Custom_Domain_Herecon il dominio URL personalizzato eEnter_the_Tenant_ID_Herecon l'ID tenant. Se non hai l'ID del tenant, scopri come leggere i dettagli del tenant. - Aggiungere una proprietà
knownAuthoritiescon un valore [Enter_the_Custom_Domain_Here].
- Aggiornare il valore della proprietà da
Dopo aver apportato le modifiche al file appsettings.json, se il dominio URL personalizzato è login.contoso.com e l'ID tenant è aaaabbbbbb-0000-cccc-1111-dddd2222eee, allora il file sarà simile al frammento di codice seguente:
{
"AzureAd": {
"Authority": "https://login.contoso.com/aaaabbbb-0000-cccc-1111-dddd2222eeee",
"ClientId": "Enter_the_Application_Id_Here",
"KnownAuthorities": ["login.contoso.com"]
}
}
Modificare il file di progetto
Vai al file sign-in-dotnet-wpf.csproj nella directory principale dell’app.
In questo file eseguire i due passaggi seguenti:
- Modificare il file sign-in-dotnet-wpf.csproj per indicare all’app di copiare il file appsettings.json nella directory di output al termine della compilazione del progetto. Aggiungere la parte di codice seguente al file sign-in-dotnet-wpf.csproj:
- Impostare il framework di destinazione sulla build windows10.0.19041.0 per facilitare la lettura del token memorizzato nella cache dalla classe di supporto dei token, come si noterà nella classe di supporto della cache dei 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>
Creare una classe helper della cache dei token
Creare una classe di supporto per la cache dei token che inizializza una cache di token. L'applicazione tenta di leggere il token dalla cache prima di tentare di acquisire un nuovo token. Se il token non viene trovato nella cache, l'applicazione acquisisce un nuovo token. Al momento della disconnessione, tutti gli account e tutti i token di accesso corrispondenti verranno rimossi dalla cache.
Creare un file TokenCacheHelper.cs nella cartella radice dell'app.
Aprire il file TokenCacheHelper.cs. Aggiungere i pacchetti e i namespace al file. Nei passaggi seguenti questo file viene popolato con la logica del codice aggiungendo la logica pertinente alla classe
TokenCacheHelper.using System.IO; using System.Security.Cryptography; using Microsoft.Identity.Client; namespace sign_in_dotnet_wpf { static class TokenCacheHelper{} }Aggiungere il costruttore alla classe
TokenCacheHelperche definisce il percorso del file della cache. Per le app desktop in un pacchetto (pacchetti MSIX, detto anche Desktop Bridge), la cartella dell’assembly in esecuzione è di sola lettura. In questo caso è necessario usareWindows.Storage.ApplicationData.Current.LocalCacheFolder.Path + "\msalcache.bin", che è una cartella di lettura/scrittura per singola app confezionata.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(); } }Aggiungere codice per gestire la serializzazione della cache dei token. L'interfaccia
ITokenCacheimplementa l'accesso pubblico alle operazioni della cache. L’interfacciaITokenCachecontiene i metodi per sottoscrivere gli eventi di serializzazione della cache, mentre l'interfacciaITokenCacheSerializerespone i metodi da usare negli eventi di serializzazione della cache, per serializzare/deserializzare la cache.TokenCacheNotificationArgscontiene i parametri usati dalla chiamataMicrosoft.Identity.Client(MSAL) che accede alla cache. L’interfacciaITokenCacheSerializerè disponibile nel callbackTokenCacheNotificationArgs.Aggiungere il codice seguente alla classe
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); }Nel metodo
BeforeAccessNotificationverrà letta la cache dal file system e, se la cache non è vuota, verrà deserializzata e caricata. Il metodoAfterAccessNotificationviene chiamato dopoMicrosoft.Identity.Client(MSAL) accede alla cache. Se la cache è stata modificata, serializzarla e rendere persistenti le modifiche alla cache.EnableSerializationcontiene i metodiITokenCache.SetBeforeAccess()eITokenCache.SetAfterAccess():-
ITokenCache.SetBeforeAccess()imposta un delegato per la ricezione delle modifiche prima che qualsiasi metodo della libreria acceda alla cache. Ciò consente al delegato di deserializzare una voce della cache per l'applicazione e gli account specificati inTokenCacheNotificationArgs. -
ITokenCache.SetAfterAccess()imposta un delegato per la ricezione delle notifiche dopo che qualsiasi metodo della libreria accede alla cache. Ciò consente al delegato di serializzare una voce della cache per l'applicazione e gli account specificati inTokenCacheNotificationArgs.
-
Creare l'interfaccia utente dell'app desktop WPF
Modificare il file MainWindow.xaml per aggiungere gli elementi dell'interfaccia utente per l'app. Aprire il file MainWindow.xaml nella cartella radice dell'app e aggiungere la parte di codice seguente con la sezione del controllo <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>
Questo codice aggiunge elementi chiave dell'interfaccia utente. I metodi e gli oggetti che gestiscono la funzionalità degli elementi dell'interfaccia utente vengono definiti nel file MainWindow.xaml.cs che verrà creato nel passaggio successivo.
- Un pulsante che permette all'utente di accedere. Quando l’utente seleziona questo pulsante viene chiamato il metodo
SignInButton_Click. - Pulsante che consente all’utente di disconnettersi. Quando l’utente seleziona questo pulsante viene chiamato il metodo
SignOutButton_Click. - Casella di testo che visualizza i dettagli del risultato dell'autenticazione dopo il tentativo di accesso da parte dell'utente. Le informazioni visualizzate qui vengono restituite dall'oggetto
ResultText. - Casella di testo che visualizza i dettagli del token dopo l'accesso da parte dell'utente. Le informazioni visualizzate qui vengono restituite dall'oggetto
TokenInfoText.
Aggiungere codice al file MainWindow.xaml.cs
Il file MainWindow.xaml.cs contiene il codice che fornisce la logica di runtime per il comportamento degli elementi dell'interfaccia utente nel file MainWindow.xaml.
Aprire il file MainWindow.xaml.cs nella cartella radice dell'app.
Aggiungere il codice seguente nel file per importare i pacchetti e definire i segnaposto per i metodi creati.
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){...} } }Aggiungere il codice seguente al metodo
SignInButton_Click. Questo metodo viene chiamato quando l'utente seleziona il pulsante Accedi.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()restituisce tutti gli account disponibili nella cache dei token utente per l'app. L'interfacciaIAccountfornisce informazioni su un singolo account.Per acquisire i token, l'app tenta di acquisire il token in modo invisibile all'utente usando il metodo
AcquireTokenSilentper verificare se la cache contiene un token accettabile. Il metodoAcquireTokenSilentpotrebbe non avere esito positivo, ad esempio, perché l'utente si è disconnesso. Se MSAL rileva che il problema può essere risolto con un'azione interattiva, attiva un'eccezioneMsalUiRequiredException. Questa eccezione fa sì che l'app acquisisca un token in modo interattivo.Se si chiama il metodo
AcquireTokenInteractive, viene visualizzata una finestra in cui viene chiesto agli utenti di eseguire l'accesso. Le app richiedono in genere agli utenti di accedere in modo interattivo la prima volta che devono eseguire l'autenticazione. Potrebbe essere necessario accedere anche quando è in corso un'operazione invisibile per acquisire un token. Dopo la prima esecuzione diAcquireTokenInteractive,AcquireTokenSilentdiventa il metodo consueto usato per ottenere i tokenAggiungere il codice seguente al metodo
SignOutButton_Click. Questo metodo viene chiamato quando l'utente seleziona il pulsante Disconnessione.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}"; } } }Il metodo
SignOutButton_Clickrimuove tutti gli account e tutti i token di accesso corrispondenti dalla cache. Al successivo tentativo di accesso, l'utente dovrà farlo in modo interattivo.Aggiungere il codice seguente al metodo
DisplayBasicTokenInfo. Questo metodo visualizza informazioni di base sul 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; } }
Aggiungere codice al file App.xaml.cs
App.xaml è dove di dichiarano le risorse usate nell'app. Si tratta del punto di ingresso per l'app. App.xaml.cs è il file code-behind per App.xaml. App.xaml.cs definisce anche la finestra iniziale per l'applicazione.
Aprire il file App.xaml.cs nella cartella radice dell'app e quindi aggiungerne il codice seguente.
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 questo passaggio verrà caricato il file appsettings.json. Il generatore di configurazione consente di leggere le configurazioni dell'app definite nel file appsettings.json. È anche possibile definire l'app WPF come app client pubblica perché si tratta di un'app desktop. Il metodo TokenCacheHelper.EnableSerialization abilita la serializzazione della cache dei token.
Eseguire l'app
Esegui l'app e effettua l'accesso per testare l'applicazione
Nel terminale, passare alla cartella radice dell'app WPF ed eseguire l'app eseguendo il comando
dotnet runnel terminale.Dopo aver avviato l'esempio, viene visualizzata in genere una finestra con un pulsante Accedi. Selezionare il pulsante Accedi.
Nella pagina di accesso immettere l'indirizzo di posta elettronica dell'account. Se non si dispone di un account, selezionare il collegamento Nessun account? Creare un account per avviare il flusso di iscrizione. Seguire questo flusso per creare un nuovo account e accedere.
Dopo l'accesso, verrà visualizzata una schermata che mostra l'accesso riuscito e le informazioni di base sull'account utente archiviato nel token recuperato. Le informazioni di base vengono visualizzate nella sezione Informazioni token della schermata di accesso