共用方式為


教學課程:使用外部租用戶在 .NET MAUI 應用程式中登入使用者

本教學課程是系列的最後一部分,示範如何在 .NET Multi-platform App UI (.NET MAUI) 殼層中新增登入和登出程式碼,以及如何在 Windows 平台上執行應用程式。 在此系列的第 2 部分中,您已建立 .NET MAUI 殼層應用程式、透過 MSAL 協助程式類別新增 MSAL SDK 支援、安裝必要的程式庫,以及包括映像資源。 這個最終步驟示範如何在 .NET MAUI 殼層中新增登入和登出程式碼,以及如何在 Windows 平台上執行應用程式。

在本教學課程中,您會了解如何:

  • 新增登入和登出程式碼。
  • 修改應用程式殼層。
  • 新增平台特定程式碼。
  • 新增應用程式設定。
  • 執行和測試 .NET MAUI 殼層應用程式。

必要條件

新增登入和登出程式碼

.NET MAUI 應用程式的使用者介面 (UI) 是由對應至各目標平台的原生控制項的物件所構成。 用來建立 .NET MAUI 應用程式 UI 的主要控制項群組包括頁面、版面配置和檢視。

新增主要檢視頁面

後續步驟將會組織我們的程式碼,以定義 main view

  1. 從專案中,刪除 MainPage.xamlMainPage.xaml.cs,因為已不再需要。 在 [方案總管] 窗格中,尋找 MainPage.xaml 的項目,並在其上按一下滑鼠右鍵,然後選取 [刪除]

  2. 以滑鼠右鍵按一下 [SignInMaui] 專案,然後選取 [新增] > [新增資料夾]。 將資料夾命名為 Views

  3. 以滑鼠右鍵按一下 [Views]

  4. 選取 [新增] > [新增項目...]

  5. 在範本清單中,選取 [.NET MAUI]

  6. 選取 [.NET MAUI ContentPage (XAML)] 範本。 將檔案命名為 MainView.xaml

  7. 選取 [新增]。

  8. MainView.xaml 檔案將會在新的文件索引標籤中開啟,並顯示可代表頁面 UI 的所有 XAML 標記。 將 XAML 標記取代為下列標記:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="SignInMaui.Views.MainView"
                 Title="Microsoft Entra External ID"
                 >
        <Shell.BackButtonBehavior>
            <BackButtonBehavior IsVisible="False" IsEnabled="False" />
        </Shell.BackButtonBehavior>
    
        <ScrollView>
            <VerticalStackLayout 
                Spacing="25" 
                Padding="30,0" 
                VerticalOptions="Center">
    
                <Image
                    Source="external_id.png"
                    SemanticProperties.Description="External ID"
                    HeightRequest="200"
                    HorizontalOptions="Center" />
    
                <Label 
                    Text="CIAM"
                    SemanticProperties.HeadingLevel="Level1"
                    FontSize="26"
                    HorizontalOptions="Center" />
    
                <Label 
                    Text="MAUI sample"
                    SemanticProperties.HeadingLevel="Level1"
                    FontSize="26"
                    HorizontalOptions="Center" />
    
                <Button 
                    x:Name="SignInButton"
                    Text="Sign In"
                    SemanticProperties.Hint="Sign In"
                    Clicked="OnSignInClicked"
                    HorizontalOptions="Center"
                    IsEnabled="False"/>
    
            </VerticalStackLayout>
        </ScrollView>
     
    </ContentPage>
    
  9. 儲存檔案。

    請細分頁面上所放置 XAML 控制項的主要部分:

    • <ContentPage> 是 MainView 類別的根物件。
    • <VerticalStackLayout> 是 ContentPage 的子物件。 此版面配置控制項會逐一垂直排列其子系。
    • <Image> 會顯示影像,而在此案例中,其會使用先前下載的 azureactive_directory.png_。
    • <Label> 會控制顯示文字。
    • 使用者可以按 <Button>,而這會引發 Clicked 事件。 您可以執行程式碼來回應 Clicked 事件。
    • Clicked="OnSignInClicked" 按鈕的 Clicked 事件會指派給 OnSignInClicked 事件處理程式,而此事件處理程式將定義於程式碼後置檔案中。 您將在下一個步驟中建立此程式碼。

處理 OnSignInClicked 事件

下一個步驟是新增按鈕 Clicked 事件的程式碼。

  1. 在 Visual Studio 的 [方案總管] 窗格中,展開 [MainView.xaml] 檔案,以顯示其程式碼後置檔案 [MainView.xaml.cs]。 開啟 MainView.xaml.cs,並將檔案的內容取代為下列程式碼:

    // Copyright (c) Microsoft Corporation. All rights reserved.
    // Licensed under the MIT License.
    
    using SignInMaui.MSALClient;
    using Microsoft.Identity.Client;
    
    namespace SignInMaui.Views
    {
        public partial class MainView : ContentPage
        {
            public MainView()
            {
                InitializeComponent();
    
                IAccount cachedUserAccount = PublicClientSingleton.Instance.MSALClientHelper.FetchSignedInUserFromCache().Result;
    
                _ = Dispatcher.DispatchAsync(async () =>
                {
                    if (cachedUserAccount == null)
                    {
                        SignInButton.IsEnabled = true;
                    }
                    else
                    {
                        await Shell.Current.GoToAsync("claimsview");
                    }
                });
            }
    
            private async void OnSignInClicked(object sender, EventArgs e)
            {
                await PublicClientSingleton.Instance.AcquireTokenSilentAsync();
                await Shell.Current.GoToAsync("claimsview");
            }
            protected override bool OnBackButtonPressed() { return true; }
    
        }
    }
    

MainView 類別是負責顯示應用程式主要檢視的內容頁面。 在建構函式中,其會使用 PublicClientSingleton 執行個體中的 MSALClientHelper 來擷取已快取的使用者帳戶,並在找不到任何已快取的使用者帳戶時啟用登入按鈕。

按一下登入按鈕時,會呼叫 AcquireTokenSilentAsync 方法以無訊息方式取得權杖,並使用 Shell.Current.GoToAsync 方法導覽至 claimsview 頁面。 此外,會覆寫 OnBackButtonPressed 方法以傳回 true,這指出已停用此檢視的返回按鈕。

新增宣告檢視頁面

後續步驟將會組織程式碼,以定義 ClaimsView 頁面。 此頁面將會顯示識別碼權杖中找到的使用者宣告。

  1. 在 Visual Studio 的 [方案總管] 中,以滑鼠右鍵按一下 [Views]

  2. 選取 [新增] > [新增項目...]

  3. 在範本清單中,選取 [.NET MAUI]

  4. 選取 [.NET MAUI ContentPage (XAML)] 範本。 將檔案命名為 ClaimsView.xaml

  5. 選取 [新增]。

  6. ClaimsView.xaml 檔案將會在新的文件索引標籤中開啟,並顯示可代表頁面 UI 的所有 XAML 標記。 將 XAML 標記取代為下列標記:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="SignInMaui.Views.ClaimsView"
                 Title="ID Token View">
        <Shell.BackButtonBehavior>
            <BackButtonBehavior IsVisible="False" IsEnabled="False" />
        </Shell.BackButtonBehavior>
        <VerticalStackLayout>
            <Label 
                Text="CIAM"
                FontSize="26"
                HorizontalOptions="Center" />
            <Label 
                Text="MAUI sample"
                FontSize="26"
                Padding="0,0,0,20"
                HorizontalOptions="Center" />
    
            <Label 
                Padding="0,20,0,0"
                VerticalOptions="Center" 
                HorizontalOptions="Center"
                FontSize="18"
                Text="Claims found in ID token"
                />
            <ListView ItemsSource="{Binding IdTokenClaims}"
                      x:Name="Claims">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid Padding="0, 0, 0, 0">
                                <Label Grid.Column="1" 
                                       Text="{Binding}" 
                                       HorizontalOptions="Center" />
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Button
                x:Name="SignOutButton"
                Text="Sign Out"
                HorizontalOptions="Center"
                Clicked="SignOutButton_Clicked" />
        </VerticalStackLayout>
    </ContentPage>
    

    此 XAML 標記程式碼代表 .NET MAUI 應用程式中宣告檢視的 UI 版面配置。 其首先會以標題定義 ContentPage,並停用返回按鈕行為。

    VerticalStackLayout 內,有數個用於顯示靜態文字的 Label 元素,後面接著名為 ClaimsListView,其繫結至名為 IdTokenClaims 的集合,以顯示識別碼權杖中找到的宣告。 每個宣告都會使用 DataTemplate 以在 ViewCell 內轉譯並顯示為方格內的置中 Label

    最後,版面配置底部中間具有 Sign Out 按鈕,而按一下該按鈕時會觸發 SignOutButton_Clicked 事件處理常式。

處理 ClaimsView 資料

下一個步驟是新增程式碼來處理 ClaimsView 資料。

  1. 在 Visual Studio 的 [方案總管] 窗格中,展開 [ClaimsView.xaml] 檔案,以顯示其程式碼後置檔案 [ClaimsView.xaml.cs]。 開啟 ClaimsView.xaml.cs,並將檔案的內容取代為下列程式碼:

    using SignInMaui.MSALClient;
    using Microsoft.Identity.Client;
    
    namespace SignInMaui.Views;
    
    public partial class ClaimsView : ContentPage
    {
        public IEnumerable<string> IdTokenClaims { get; set; } = new string[] {"No claims found in ID token"};
        public ClaimsView()
        {
            BindingContext = this;
            InitializeComponent();
    
            _ = SetViewDataAsync();
        }
    
        private async Task SetViewDataAsync()
        {
            try
            {
                _ = await PublicClientSingleton.Instance.AcquireTokenSilentAsync();
    
                IdTokenClaims = PublicClientSingleton.Instance.MSALClientHelper.AuthResult.ClaimsPrincipal.Claims.Select(c => c.Value);
    
                Claims.ItemsSource = IdTokenClaims;
            }
    
            catch (MsalUiRequiredException)
            {
                await Shell.Current.GoToAsync("claimsview");
            }
        }
    
        protected override bool OnBackButtonPressed() { return true; }
    
        private async void SignOutButton_Clicked(object sender, EventArgs e)
        {
            await PublicClientSingleton.Instance.SignOutAsync().ContinueWith((t) =>
            {
                return Task.CompletedTask;
            });
    
            await Shell.Current.GoToAsync("mainview");
        }
    }
    

    ClaimsView.xaml.cs 程式碼代表 .NET MAUI 應用程式中宣告檢視的程式碼後置。 其首先會匯入必要的命名空間,並定義用於擴充 ContentPageClaimsView 類別。 IdTokenClaims 屬性為可列舉的字串,一開始設定為單一字串,指出找不到宣告。

    ClaimsView 建構函式會將繫結內容設定為目前的執行個體、初始化檢視元件,並以非同步方式呼叫 SetViewDataAsync 方法。 SetViewDataAsync 方法會嘗試以無訊息方式取得權杖、從驗證結果中擷取宣告,並設定 IdTokenClaims 屬性,以在名為 ClaimsListView 中顯示這些宣告。 如果發生 MsalUiRequiredException (指出需要使用者互動以進行驗證),則應用程式會導覽至宣告檢視。

    OnBackButtonPressed 方法會覆寫返回按鈕行為以一律傳回 True,避免使用者從此檢視返回。 SignOutButton_Clicked 事件處理常式會使用 PublicClientSingleton 執行個體來登出使用者,並在完成時導覽至 main view

修改應用程式殼層

AppShell 類別會定義應用程式的視覺效果階層,即用於建立應用程式 UI 的 XAML 標記。 更新 AppShell,以讓其知道 Views

  1. 在 [方案總管] 窗格中,按兩下 AppShell.xaml 檔案以開啟 XAML 編輯器。 將 XAML 標記取代為下列程式碼:

    <?xml version="1.0" encoding="UTF-8" ?>
    <Shell
        x:Class="SignInMaui.AppShell"
        xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:SignInMaui.Views"
        Shell.FlyoutBehavior="Disabled">
    
        <ShellContent
            Title="Home"
            ContentTemplate="{DataTemplate local:MainView}"
            Route="MainPage" />
    </Shell>
    

    XAML 程式碼會定義可停用飛出視窗行為的 AppShell 類別,並將主要內容設定為標題為 Home 且內容範本指向 MainView 類別的 ShellContent 元素。

  2. 在 Visual Studio 的 [方案總管] 窗格中,展開 [AppShell.xaml] 檔案,以顯示其程式碼後置檔案 [AppShell.xaml.cs]。 開啟 [AppShell.xaml.cs],然後將檔案的內容取代為下列程式碼:

    // Copyright (c) Microsoft Corporation. All rights reserved.
    // Licensed under the MIT License.
    using SignInMaui.Views;
    
    namespace SignInMaui;
    
    public partial class AppShell : Shell
    {
        public AppShell()
        {
            InitializeComponent();
            Routing.RegisterRoute("mainview", typeof(MainView));
            Routing.RegisterRoute("claimsview", typeof(ClaimsView));
        }
    }
    

    您會更新 AppShell.xaml.cs 檔案,以包括 MainViewClaimsView 的必要路由註冊。 呼叫 InitializeComponent() 方法,以確定 AppShell 類別的初始化。 RegisterRoute() 方法會將 mainviewclaimsview 路由與其各自的檢視類型 (MainViewClaimsView) 產生關聯。

新增平台特定程式碼

.NET MAUI 應用程式專案包含 [平台] 資料夾,其中每個子資料夾代表 .NET MAUI 可以將其設為目標的平台。 若要提供應用程式特定行為以補充預設應用程式類別,您可以修改 Platforms/Windows/App.xaml.cs

將檔案的內容取代為下列程式碼:

using SignInMaui.MSALClient;
using Microsoft.Identity.Client;
using Microsoft.UI.Xaml;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace SignInMaui.WinUI;

/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : MauiWinUIApplication
{
    /// <summary>
    /// Initializes the singleton application object.  This is the first line of authored code
    /// executed, and as such is the logical equivalent of main() or WinMain().
    /// </summary>
    public App()
    {
        this.InitializeComponent();

        // configure redirect URI for your application
        PlatformConfig.Instance.RedirectUri = $"msal{PublicClientSingleton.Instance.MSALClientHelper.AzureAdConfig.ClientId}://auth";

        // Initialize MSAL
        IAccount existinguser = Task.Run(async () => await PublicClientSingleton.Instance.MSALClientHelper.InitializePublicClientAppAsync()).Result;

    }

    protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        base.OnLaunched(args);

        var app = SignInMaui.App.Current;
        PlatformConfig.Instance.ParentWindow = ((MauiWinUIWindow)app.Windows[0].Handler.PlatformView).WindowHandle;
    }
}

在程式碼中,您會設定應用程式的重新導向 URI,並初始化 MSAL,然後設定應用程式的父視窗。 此外,您可以覆寫 OnLaunched 方法來處理啟動事件,並擷取父視窗控制代碼。

新增應用程式設定

設定允許區隔可設定應用程式與程式碼行為的資料,並允許變更行為,而不需要重建應用程式。 MauiAppBuilder 會提供 ConfigurationManager 以在 .NET MAUI 應用程式中進行設定。 請將 appsettings.json 檔案新增為 EmbeddedResource

若要建立 appsettings.json,請遵循下列步驟:

  1. 在 Visual Studio 的 [方案總管] 窗格中,以滑鼠右鍵按一下 [SignInMaui] 專案 > [新增] > [新增項目...]

  2. 選取 [Web] > [JavaScript JSON 設定檔]。 將檔案命名為 appsettings.json

  3. 選取 [新增]。

  4. 選取 [appsettings.json]

  5. 在 [屬性] 窗格中,將 [建置動作] 設定為 [內嵌資源]

  6. 在 [屬性] 窗格中,將 [複製到輸出目錄] 設定為 [永遠複製]

  7. appsettings.json 檔案的內容取代為下列程式碼:

    {
      "AzureAd": {
        "Authority": "https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/",
        "ClientId": "Enter_the_Application_Id_Here",
        "CacheFileName": "msal_cache.txt",
        "CacheDir": "C:/temp"
      },
      "DownstreamApi": {
        "Scopes": "openid offline_access"
      }
    }
    
  8. appsettings.json 中,找到預留位置:

    1. Enter_the_Tenant_Subdomain_Here,並將其取代為目錄 (租用戶) 子網域。 例如,若租用戶主要網域是 contoso.onmicrosoft.com,請使用 contoso。 如果您沒有租用戶名稱,請了解如何讀取租用戶詳細資料
    2. Enter_the_Application_Id_Here,並將其取代為您稍早所註冊應用程式的應用程式 (用戶端) 識別碼。

使用自訂 URL 網域 (選用)

使用自訂網域對驗證 URL 進行完整品牌化。 就使用者而言,使用者在驗證過程中一直停留在您的網域中,而不會重新導向至 ciamlogin.com 網域名稱。

遵循下列步驟來使用 自訂網域:

  1. 使用針對外部租用戶中的應用程式啟用自訂 URL 網域中的步驟,為外部租用戶啟用自訂 URL 網域。

  2. 開啟 appsettings.json 檔案:

    1. Authority 屬性的值更新為 https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Here。 以您的自訂 URL 網域取代 Enter_the_Custom_Domain_Here,並以您的租用戶識別碼取代 Enter_the_Tenant_ID_Here。 如果您沒有租用戶識別碼,請了解如何讀取租用戶詳細資料
    2. 新增具有值 [Enter_the_Custom_Domain_Here]knownAuthorities 屬性。

appsettings.json 檔案進行變更之後,如果您的自訂 URL 網域為 login.contoso.com,且您的租用戶識別碼為 aaaabbbb-0000-cccc-1111-dddd2222eeee,則您的檔案看起來應該類似以下程式碼片段:

{
  "AzureAd": {
    "Authority": "https://login.contoso.com/aaaabbbb-0000-cccc-1111-dddd2222eeee",
    "ClientId": "Enter_the_Application_Id_Here",
    "CacheFileName": "msal_cache.txt",
    "CacheDir": "C:/temp",
    "KnownAuthorities": ["login.contoso.com"]
  },
  "DownstreamApi": {
    "Scopes": "openid offline_access"
  }
}

執行和測試 .NET MAUI 傳統型應用程式

.NET MAUI 應用程式已設計為可在多個作業系統和裝置上執行。 您將需要選取想要用來測試並偵錯應用程式的目標。

將 Visual Studio 工具列中的 [偵錯目標] 設定為您想要進行偵錯和測試的裝置。 下列步驟示範如何將 [偵錯目標] 設定為 [Windows]

  1. 選取 [偵錯目標] 下拉式清單。
  2. 選取 [架構]
  3. 選取 [net7.0-windows...]

按 F5 或選取 Visual Studio 頂端的「播放按鈕」,以執行應用程式。

  1. 您現在可以測試範例 .NET MAUI 傳統型應用程式。 執行應用程式之後,傳統型應用程式視窗會自動出現:

    螢幕擷取畫面:傳統型應用程式中的登入按鈕

  2. 在出現的傳統型視窗上,選取 [登入] 按鈕。 瀏覽器視窗隨即開啟,而且系統會提示您登入。

    螢幕擷取畫面:在傳統型應用程式中輸入認證的使用者提示。

    在登入程序期間,系統會提示您授與各種權限 (允許應用程式存取您的資料)。 成功登入並同意時,應用程式畫面會顯示主頁面。

    螢幕擷取畫面:登入後傳統型應用程式中的主頁面。

後續步驟