Oktatóanyag: Windows Hello bejelentkezési alkalmazás létrehozása

Ez az első része annak a teljes útmutatónak, amely bemutatja, hogyan hozhat létre olyan csomagolt Windows app, amely Windows Hello használ a hagyományos felhasználónév- és jelszó-hitelesítési rendszerek alternatívaként. Ebben az esetben az alkalmazás egy WinUI 3-alkalmazás, de ugyanez a megközelítés használható bármilyen csomagolt Windows app, beleértve a WPF és Windows Forms alkalmazásokat is. Az alkalmazás felhasználónevet használ a bejelentkezéshez, és minden fiókhoz létrehoz egy Hello-kulcsot. Ezeket a fiókokat a Windows Hello konfigurációjára vonatkozó Windows-beállításokban beállított PIN-kód védi.

Ez az útmutató két részre oszlik: az alkalmazás kiépítésére és a háttérszolgáltatás csatlakoztatására. Ha befejezte ezt a cikket, folytassa a 2. résztel: Windows Hello bejelentkezési szolgáltatás.

Mielőtt hozzákezdene, olvassa el a Windows Hello áttekintést a Windows Hello működésének általános megértéséhez.

Első lépések

A project létrehozásához szüksége lesz némi tapasztalatra a C# és az XAML használatával kapcsolatban. A Visual Studio 2026-ot Windows 10 vagy Windows 11 rendszerű gépen is használnia kell. A fejlesztési környezet beállításával kapcsolatos útmutatásért tekintse meg a Windows-alkalmazások fejlesztésének megkezdését ismertető cikket.

  • A Visual Studio válassza a File>New>Project lehetőséget.
  • A Új Project párbeszédpanel legördülő szűrőiben válassza a C#/C++, Windows és WinUI lehetőséget.
  • Válassza a WinUI Blank App (Csomagolt) lehetőséget , és adja az alkalmazásnak a "WindowsHelloLogin" nevet.
  • Az új alkalmazás (F5) létrehozása és futtatásakor egy üres ablaknak kell megjelennie a képernyőn. Zárja be az alkalmazást.

A képernyőkép az első alkalommal futó új Windows Hello bejelentkezési alkalmazásról

1. gyakorlat: Bejelentkezés a Windows Hello-val

Ebben a gyakorlatban megtudhatja, hogyan ellenőrizheti, hogy Windows Hello van-e beállítva a gépen, és hogyan jelentkezhet be egy fiókba Windows Hello használatával.

  • Az új projektben hozzon létre egy "Nézetek" nevű új mappát a megoldásban. Ez a mappa tartalmazza azokat a lapokat, amelyekre a mintában navigálni fog. Kattintson a jobb gombbal a project a Solution Explorer területen, válassza a Add>Új mappa lehetőséget, majd nevezze át a mappát Nézetek.

    Egy nézetek nevű új mappa hozzáadása a Windows Hello bejelentkezési projekthez

  • Nyissa meg a MainWindow.xaml fájlt, és cserélje le a Window tartalmat egy üres StackPanel vagy Grid vezérlőre. A MainWindow betöltésekor az oldalnavigációt implementáljuk, és egy új lapra navigálunk, így nincs szükségünk tartalomra a MainWindow-ban.

  • Adjon hozzá egy tulajdonságot Title a MainWindow-hoz az XAML-ben. Az attribútumnak így kell kinéznie: Title="Windows Hello Login".

  • Az összeállítási hibák elkerülése érdekében távolítsa el a myButton_Click eseménykezelőt a MainWindow.xaml.cs-ből. Ehhez a mintához nincs szükség eseménykezelőre.

  • Kattintson a jobb gombbal az új Nézetek mappára, válassza azÚj elem>, majd az Üres lap sablon lehetőséget. Nevezze el ezt az oldalt "MainPage.xaml".

    A Windows Hello Bejelentkezési projekthez egy új üres oldal hozzáadásának képernyőképe

  • Nyissa meg a App.xaml.cs fájlt, és frissítse az OnLaunched kezelőt az alkalmazás oldalnavigációjának implementálásához. Hozzá kell adnia egy RootFrame_NavigationFailed kezelőmetódust is a lapok betöltése során előforduló hibák kezeléséhez.

    protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
    {
        m_window = new MainWindow();
        var rootFrame = new Frame();
        rootFrame.NavigationFailed += RootFrame_NavigationFailed;
        rootFrame.Navigate(typeof(MainPage), args);
        m_window.Content = rootFrame;
        m_window.Activate();
    }
    
    private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
    {
        throw new Exception($"Error loading page {e.SourcePageType.FullName}");
    }
    
  • A kód fordítási hibáinak megoldásához négy utasítást is fel kell vennie a App.xaml.cs fájl tetejére.

    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Navigation;
    using System;
    using WindowsHelloLogin.Views;
    
  • Kattintson a jobb gombbal az új Nézetek mappára, válassza azÚj elem>, majd az Üres lap sablon lehetőséget. Nevezze el ezt az oldalt "Login.xaml".

  • Az új bejelentkezési oldal felhasználói felületének meghatározásához adja hozzá a következő XAML-t. Ez az XAML a StackPanel a következő elemek igazítását határozza meg.

    • Olyan TextBlock, amely tartalmazni fog egy címet.

    • A TextBlock hibaüzenetekhez.

    • A TextBox a felhasználónév beviteléhez.

    • A Button a regisztrációs lapra való navigáláshoz.

    • Egy TextBlock, amely a(z) Windows Hello állapotát tartalmazza.

    • A TextBlock bejelentkezési lap magyarázata, mivel még nincsenek háttérbeli vagy konfigurált felhasználók.

      <Grid>
        <StackPanel>
          <TextBlock Text="Login" FontSize="36" Margin="4" TextAlignment="Center"/>
          <TextBlock x:Name="ErrorMessage" Text="" FontSize="20" Margin="4" Foreground="Red" TextAlignment="Center"/>
          <TextBlock Text="Enter your username below" Margin="0,0,0,20"
                     TextWrapping="Wrap" Width="300"
                     TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
          <TextBox x:Name="UsernameTextBox" Margin="4" Width="250"/>
          <Button x:Name="LoginButton" Content="Login" Background="DodgerBlue" Foreground="White"
                  Click="LoginButton_Click" Width="80" HorizontalAlignment="Center" Margin="0,20"/>
          <TextBlock Text="Don't have an account?"
                     TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
          <TextBlock x:Name="RegisterButtonTextBlock" Text="Register now"
                     PointerPressed="RegisterButtonTextBlock_OnPointerPressed"
                     Foreground="DodgerBlue"
                     TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
          <Border x:Name="WindowsHelloStatus" Background="#22B14C"
                  Margin="0,20" Height="100" >
            <TextBlock x:Name="WindowsHelloStatusText" Text="Windows Hello is ready to use!"
                       Margin="4" TextAlignment="Center" VerticalAlignment="Center" FontSize="20"/>
          </Border>
          <TextBlock x:Name="LoginExplanation" FontSize="24" TextAlignment="Center" TextWrapping="Wrap" 
                     Text="Please Note: To demonstrate a login, validation will only occur using the default username 'sampleUsername'"/>
        </StackPanel>
      </Grid>
      
  • A megoldás létrehozásához néhány metódust hozzá kell adni a kód mögötti fájlhoz. Nyomja le az F7 billentyűt, vagy használja a Solution Explorer a Login.xaml.cs fájl szerkesztéséhez. Adja hozzá a következő két eseménymetelyt a bejelentkezési és regisztrációs események kezeléséhez. Egyelőre ezek a metódusok üres sztringre állítják be a ErrorMessage.Text metódust. Ügyeljen arra, hogy az alábbi utasításokat használja. A következő lépésekhez szükség lesz rájuk.

    using Microsoft.UI.Xaml;
    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Input;
    using Microsoft.UI.Xaml.Media;
    using Microsoft.UI.Xaml.Navigation;
    using System;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class Login : Page
        {
            public Login()
            {
                this.InitializeComponent();
            }
            private void LoginButton_Click(object sender, RoutedEventArgs e)
            {
                ErrorMessage.Text = "";
            }
            private void RegisterButtonTextBlock_OnPointerPressed(object sender, PointerRoutedEventArgs e)
            {
                ErrorMessage.Text = "";
            }
        }
    }
    
  • A Bejelentkezési lap megjelenítéséhez szerkessze a MainPage-kódot, hogy a MainPage betöltésekor a Bejelentkezési lapra navigáljon. Nyissa meg a MainPage.xaml.cs fájlt. Solution Explorer-ben kattintson duplán a MainPage.xaml.cs fájlra. Ha ezt nem találja, kattintson a MainPage.xaml melletti kis nyílra a kód mögötti fájl megjelenítéséhez. Hozzon létre egy betöltött eseménykezelő metódust, amely a Bejelentkezési lapra navigál.

    namespace WindowsHelloLogin.Views
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
                Loaded += MainPage_Loaded;
            }
    
            private void MainPage_Loaded(object sender, RoutedEventArgs e)
            {
                Frame.Navigate(typeof(Login));
            }
        }
    }
    
  • A Login lapon kezelnie kell a OnNavigatedTo eseményt annak ellenőrzéséhez, hogy Windows Hello elérhető-e az aktuális gépen. Az Login.xaml.cs implementálja a következő kódot. Megfigyelheti, hogy a WindowsHelloHelper objektum azt jelzi, hogy hiba történt. Ennek az az oka, hogy még nem hoztuk létre ezt a segédosztályt.

    public sealed partial class Login : Page
    {
        public Login()
        {
            this.InitializeComponent();
        }
    
        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            // Check if Windows Hello is set up and available on this machine
            if (await WindowsHelloHelper.WindowsHelloAvailableCheckAsync())
            {
            }
            else
            {
                // Windows Hello isn't set up, so inform the user
                WindowsHelloStatus.Background = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 50, 170, 207));
                WindowsHelloStatusText.Text = $"Windows Hello is not set up!{Environment.NewLine}Please go to Windows Settings and set up a PIN to use it.";
                LoginButton.IsEnabled = false;
            }
        }
    }
    
  • A WindowsHelloHelper osztály létrehozásához kattintson a jobb gombbal a WindowsHelloLogin project, majd a Add>Új mappa elemre. Nevezze el ezt a mappát Utils néven.

  • Kattintson a jobb gombbal a Utils mappára, és válassza azOsztály>. Az új osztály neve "WindowsHelloHelper.cs".

    A Windows Hello bejelentkezési segédosztály létrehozásának képernyőképe

  • Módosítsa a WindowsHelloHelper osztály hatókörét public static, majd adja hozzá a következő módszert, amely tájékoztatja a felhasználót arról, hogy Windows Hello készen áll-e a használatra. Hozzá kell adnia a szükséges névtereket.

    using System;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using Windows.Security.Credentials;
    
    namespace WindowsHelloLogin.Utils
    {
        public static class WindowsHelloHelper
        {
            /// <summary>
            /// Checks to see if Windows Hello is ready to be used.
            /// 
            /// Windows Hello has dependencies on:
            ///     1. Having a connected Microsoft Account
            ///     2. Having a Windows PIN set up for that account on the local machine
            /// </summary>
            public static async Task<bool> WindowsHelloAvailableCheckAsync()
            {
                bool keyCredentialAvailable = await KeyCredentialManager.IsSupportedAsync();
                if (keyCredentialAvailable == false)
                {
                    // Key credential is not enabled yet as user 
                    // needs to connect to a Microsoft Account and select a PIN in the connecting flow.
                    Debug.WriteLine("Windows Hello is not set up!\nPlease go to Windows Settings and set up a PIN to use it.");
                    return false;
                }
    
                return true;
            }
        }
    }
    
  • A Login.xaml.cs adjon hozzá egy hivatkozást a WindowsHelloLogin.Utils névtérhez. Ez megoldja a metódus hibáját OnNavigatedTo .

    using WindowsHelloLogin.Utils;
    
  • Hozza létre és futtassa az alkalmazást. Ekkor a bejelentkezési lapra navigál, és a Windows Hello szalagcím jelzi, hogy Windows Hello készen áll-e a használatra. A számítógépén a Windows Hello állapotát jelző zöld vagy kék sávnak kellene megjelennie.

    A Windows Hello bejelentkezési képernyő képernyőképe kész állapottal

  • A következő lépés a bejelentkezés logikájának létrehozása. Hozzon létre egy új mappát a "Models" nevű project.

  • A Modellek mappában hozzon létre egy "Account.cs" nevű új osztályt. Ez az osztály lesz a fiókmodellje. Mivel ez egy minta project, csak egy felhasználónevet fog tartalmazni. Módosítsa az osztály hatókörét, public és adja hozzá a tulajdonságot Username .

    namespace WindowsHelloLogin.Models
    {
        public class Account
        {
            public string Username { get; set; }
        }
    }
    
  • Az alkalmazásnak módot kell adni a fiókok kezelésére. Ehhez a tesztkörnyezethez, mivel nincs kiszolgáló vagy adatbázis, a rendszer helyileg menti és betölti a felhasználók listáját. Kattintson a jobb gombbal a Utils mappára, és adjon hozzá egy új osztályt "AccountHelper.cs" néven. Az osztály hatókörét public static változtatni. Az AccountHelper egy statikus osztály, amely tartalmazza a fiókok helyi mentéséhez és betöltéséhez szükséges összes metódust. A mentés és a betöltés XmlSerializer használatával működik. A mentett fájlra és a mentési helyre is emlékeznie kell.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml.Serialization;
    using Windows.Storage;
    using WindowsHelloLogin.Models;
    
    namespace WindowsHelloLogin.Utils
    {
        public static class AccountHelper
        {
            // In the real world this would not be needed as there would be a server implemented that would host a user account database.
            // For this tutorial we will just be storing accounts locally.
            private const string USER_ACCOUNT_LIST_FILE_NAME = "accountlist.txt";
            private static string _accountListPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, USER_ACCOUNT_LIST_FILE_NAME);
            public static List<Account> AccountList = [];
    
            /// <summary>
            /// Create and save a useraccount list file. (Updating the old one)
            /// </summary>
            private static async void SaveAccountListAsync()
            {
                string accountsXml = SerializeAccountListToXml();
    
                if (File.Exists(_accountListPath))
                {
                    StorageFile accountsFile = await StorageFile.GetFileFromPathAsync(_accountListPath);
                    await FileIO.WriteTextAsync(accountsFile, accountsXml);
                }
                else
                {
                    StorageFile accountsFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(USER_ACCOUNT_LIST_FILE_NAME);
                    await FileIO.WriteTextAsync(accountsFile, accountsXml);
                }
            }
    
            /// <summary>
            /// Gets the useraccount list file and deserializes it from XML to a list of useraccount objects.
            /// </summary>
            /// <returns>List of useraccount objects</returns>
            public static async Task<List<Account>> LoadAccountListAsync()
            {
                if (File.Exists(_accountListPath))
                {
                    StorageFile accountsFile = await StorageFile.GetFileFromPathAsync(_accountListPath);
    
                    string accountsXml = await FileIO.ReadTextAsync(accountsFile);
                    DeserializeXmlToAccountList(accountsXml);
                }
    
                return AccountList;
            }
    
            /// <summary>
            /// Uses the local list of accounts and returns an XML formatted string representing the list
            /// </summary>
            /// <returns>XML formatted list of accounts</returns>
            public static string SerializeAccountListToXml()
            {
                var xmlizer = new XmlSerializer(typeof(List<Account>));
                var writer = new StringWriter();
                xmlizer.Serialize(writer, AccountList);
    
                return writer.ToString();
            }
    
            /// <summary>
            /// Takes an XML formatted string representing a list of accounts and returns a list object of accounts
            /// </summary>
            /// <param name="listAsXml">XML formatted list of accounts</param>
            /// <returns>List object of accounts</returns>
            public static List<Account> DeserializeXmlToAccountList(string listAsXml)
            {
                var xmlizer = new XmlSerializer(typeof(List<Account>));
                TextReader textreader = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(listAsXml)));
    
                return AccountList = (xmlizer.Deserialize(textreader)) as List<Account>;
            }
        }
    }
    
  • Ezután implementáljon egy módot egy fiók hozzáadására és eltávolítására a helyi fiókok listájából. Ezek a műveletek külön-külön mentik a listát. Az utolsó módszer, amire szüksége van ebben a gyakorlati laborban, egy érvényesítési módszer. Mivel nincs engedélyezési kiszolgáló vagy a felhasználók adatbázisa, ez egyetlen, szigorúan kódolt felhasználón lesz érvényesítve. Ezeket a metódusokat hozzá kell adni az AccountHelper osztályhoz.

    public static Account AddAccount(string username)
    {
        // Create a new account with the username
        var account = new Account() { Username = username };
        // Add it to the local list of accounts
        AccountList.Add(account);
        // SaveAccountList and return the account
        SaveAccountListAsync();
        return account;
    }
    
    public static void RemoveAccount(Account account)
    {
        // Remove the account from the accounts list
        AccountList.Remove(account);
        // Re save the updated list
        SaveAccountListAsync();
    }
    
    public static bool ValidateAccountCredentials(string username)
    {
        // In the real world, this method would call the server to authenticate that the account exists and is valid.
        // However, for this tutorial, we'll just have an existing sample user that's named "sampleUsername".
        // If the username is null or does not match "sampleUsername" validation will fail. 
        // In this case, the user should register a new Windows Hello user.
    
        if (string.IsNullOrEmpty(username))
        {
            return false;
        }
    
        if (!string.Equals(username, "sampleUsername"))
        {
            return false;
        }
    
        return true;
    }
    
  • A következő lépés a felhasználó bejelentkezési kérésének kezelése. A Login.xaml.cs hozzon létre egy új privát változót, amely az aktuális fiók bejelentkezését fogja tárolni. Ezután adjon hozzá egy új, SignInWindowsHelloAsync nevű metódust. Ez az AccountHelper.ValidateAccountCredentials metódussal ellenőrzi a fiók hitelesítő adatait. Ez a metódus logikai értéket ad vissza, ha a megadott felhasználónév megegyezik az előző lépésben konfigurált, kemény kóddal megadott sztringértékel. A minta hard-coded értéke "sampleUsername".

    using WindowsHelloLogin.Models;
    using WindowsHelloLogin.Utils;
    using System.Diagnostics;
    using System.Threading.Tasks;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class Login : Page
        {
            private Account _account;
    
            public Login()
            {
                this.InitializeComponent();
            }
    
            protected override async void OnNavigatedTo(NavigationEventArgs e)
            {
                // Check if Windows Hello is set up and available on this machine
                if (await WindowsHelloHelper.WindowsHelloAvailableCheckAsync())
                {
                }
                else
                {
                    // Windows Hello is not set up, so inform the user
                    WindowsHelloStatus.Background = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 50, 170, 207));
                    WindowsHelloStatusText.Text = "Windows Hello is not set up!\nPlease go to Windows Settings and set up a PIN to use it.";
                    LoginButton.IsEnabled = false;
                }
            }
    
            private async void LoginButton_Click(object sender, RoutedEventArgs e)
            {
                ErrorMessage.Text = "";
                await SignInWindowsHelloAsync();
            }
    
            private void RegisterButtonTextBlock_OnPointerPressed(object sender, PointerRoutedEventArgs e)
            {
                ErrorMessage.Text = "";
            }
    
            private async Task SignInWindowsHelloAsync()
            {
                if (AccountHelper.ValidateAccountCredentials(UsernameTextBox.Text))
                {
                    // Create and add a new local account
                    _account = AccountHelper.AddAccount(UsernameTextBox.Text);
                    Debug.WriteLine("Successfully signed in with traditional credentials and created local account instance!");
    
                    //if (await WindowsHelloHelper.CreateWindowsHelloKeyAsync(UsernameTextBox.Text))
                    //{
                    //    Debug.WriteLine("Successfully signed in with Windows Hello!");
                    //}
                }
                else
                {
                    ErrorMessage.Text = "Invalid Credentials";
                }
            }
        }
    }
    
  • Előfordulhat, hogy észrevette a Megjegyzés kódot, amely egy metódusra hivatkozik a WindowsHelloHelperben. A WindowsHelloHelper.cs adjon hozzá egy új, CreateWindowsHelloKeyAsync nevű metódust. Ez a metódus a KeyCredentialManager Windows Hello API-t használja. A RequestCreateAsync hívása létrehoz egy Windows Hello kulcsot, amely a accountId és a helyi gépre jellemző. Amennyiben érdeklődik a valós világban történő megvalósítás iránt, vegye figyelembe a switch utasításban szereplő megjegyzéseket.

    /// <summary>
    /// Creates a Windows Hello key on the machine using the account ID provided.
    /// </summary>
    /// <param name="accountId">The account ID associated with the account that we are enrolling into Windows Hello</param>
    /// <returns>Boolean indicating if creating the Windows Hello key succeeded</returns>
    public static async Task<bool> CreateWindowsHelloKeyAsync(string accountId)
    {
        KeyCredentialRetrievalResult keyCreationResult = await KeyCredentialManager.RequestCreateAsync(accountId, KeyCredentialCreationOption.ReplaceExisting);
    
        switch (keyCreationResult.Status)
        {
            case KeyCredentialStatus.Success:
                Debug.WriteLine("Successfully created key");
    
                // In the real world, authentication would take place on a server.
                // So, every time a user migrates or creates a new Windows Hello
                // account, details should be pushed to the server.
                // The details that would be pushed to the server include:
                // The public key, keyAttestation (if available), 
                // certificate chain for attestation endorsement key (if available),  
                // status code of key attestation result: keyAttestationIncluded or 
                // keyAttestationCanBeRetrievedLater and keyAttestationRetryType.
                // As this sample has no concept of a server, it will be skipped for now.
                // For information on how to do this, refer to the second sample.
    
                // For this sample, just return true
                return true;
            case KeyCredentialStatus.UserCanceled:
                Debug.WriteLine("User cancelled sign-in process.");
                break;
            case KeyCredentialStatus.NotFound:
                // User needs to set up Windows Hello
                Debug.WriteLine("Windows Hello is not set up!\nPlease go to Windows Settings and set up a PIN to use it.");
                break;
            default:
                break;
        }
    
        return false;
    }
    
  • Most, hogy létrehozta a CreateWindowsHelloKeyAsync metódust, térjen vissza a Login.xaml.cs fájlhoz, és oldja fel a kódot a SignInWindowsHelloAsync metódusban.

    private async void SignInWindowsHelloAsync()
    {
        if (AccountHelper.ValidateAccountCredentials(UsernameTextBox.Text))
        {
            //Create and add a new local account
            _account = AccountHelper.AddAccount(UsernameTextBox.Text);
            Debug.WriteLine("Successfully signed in with traditional credentials and created local account instance!");
    
            if (await WindowsHelloHelper.CreateWindowsHelloKeyAsync(UsernameTextBox.Text))
            {
                Debug.WriteLine("Successfully signed in with Windows Hello!");
            }
        }
        else
        {
            ErrorMessage.Text = "Invalid Credentials";
        }
    }
    
  • Hozza létre és futtassa az alkalmazást. A Bejelentkezési oldalra lesz irányítva. Adja meg a felhasználónevet "sampleUsername" névként, és kattintson a bejelentkezésre. A rendszer megjelenít egy Windows Hello üzenetet, amely arra kéri, hogy adja meg a PIN-kódját. A PIN-kód helyes megadása után a CreateWindowsHelloKeyAsync metódus létrehozhat egy Windows Hello kulcsot. Figyelje meg a kimeneti ablakokat, hogy megjelenjenek-e a sikerességet jelző üzenetek.

    A Windows Hello bejelentkezési pin-üzenet képernyőképe

2. gyakorlat: Üdvözlő- és felhasználói kijelölési lapok

Ebben a gyakorlatban Ön folytatja az előző gyakorlatot. Amikor egy felhasználó sikeresen bejelentkezik, egy üdvözlőoldalra kell vinni, ahol kijelentkezhet vagy törölheti a fiókját. Mivel Windows Hello minden géphez létrehoz egy kulcsot, létre lehet hozni egy felhasználóválasztó képernyőt, amely megjeleníti a gépen bejelentkezett összes felhasználót. A felhasználók ezután kiválaszthatnak egyet ezek közül a fiókok közül, és közvetlenül az üdvözlőképernyőre léphetnek anélkül, hogy újra be kellene írniuk a jelszót, mivel már hitelesítették a gép access.

  • A Nézetek mappában adjon hozzá egy új, "Welcome.xaml" nevű üres lapot. Adja hozzá a következő XAML-t a lap felhasználói felületének befejezéséhez. Ekkor megjelenik egy cím, a bejelentkezett felhasználónév és két gomb. Az egyik gomb visszakerül egy felhasználói listára (amelyet később fog létrehozni), a másik gomb pedig a felhasználó elfelejtését fogja kezelni.

    <Grid>
      <StackPanel>
        <TextBlock x:Name="Title" Text="Welcome" FontSize="40" TextAlignment="Center"/>
        <TextBlock x:Name="UserNameText" FontSize="28" TextAlignment="Center"/>
    
        <Button x:Name="BackToUserListButton" Content="Back to User List" Click="Button_Restart_Click"
                HorizontalAlignment="Center" Margin="0,20" Foreground="White" Background="DodgerBlue"/>
    
        <Button x:Name="ForgetButton" Content="Forget Me" Click="Button_Forget_User_Click"
                Foreground="White"
                Background="Gray"
                HorizontalAlignment="Center"/>
      </StackPanel>
    </Grid>
    
  • A Welcome.xaml.cs kód mögötti fájlban adjon hozzá egy új privát változót, amely a bejelentkezett fiókot fogja tárolni. Implementálnia kell egy metódust az OnNavigateTo esemény felülbírálásához, ez tárolja az üdvözlőlapra átadott fiókot. Önnek is implementálnia kell a Click eseményt a XAML-ben meghatározott két gombhoz. A WindowsHelloLogin.Models és WindowsHelloLogin.Utils névterekhez megfelelő 'using' utasításokat kell hozzáadnia.

    using WindowsHelloLogin.Models;
    using WindowsHelloLogin.Utils;
    using System.Diagnostics;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class Welcome : Page
        {
            private Account _activeAccount;
    
            public Welcome()
            {
                InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                _activeAccount = (Account)e.Parameter;
                if (_activeAccount != null)
                {
                    UserNameText.Text = _activeAccount.Username;
                }
            }
    
            private void Button_Restart_Click(object sender, RoutedEventArgs e)
            {
            }
    
            private void Button_Forget_User_Click(object sender, RoutedEventArgs e)
            {
                // Remove the account from Windows Hello
                // WindowsHelloHelper.RemoveWindowsHelloAccountAsync(_activeAccount);
    
                // Remove it from the local accounts list and re-save the updated list
                AccountHelper.RemoveAccount(_activeAccount);
    
                Debug.WriteLine($"User {_activeAccount.Username} deleted.");
            }
        }
    }
    
  • Lehet, hogy feltűnhetett Önnek egy kikommentelt sor a Button_Forget_User_Click eseménykezelőben. Eltávolítják a fiókot a helyi listáról, de jelenleg nincs mód a Windows Hello-ról való eltávolításra. Új módszert kell implementálnia a WindowsHelloHelper.cs, amely egy Windows Hello felhasználó eltávolítását fogja kezelni. Ez a módszer más Windows Hello API-kat használ a fiók megnyitásához és törléséhez. A valós világban, amikor töröl egy fiókot, a kiszolgálót vagy az adatbázist értesíteni kell, hogy a felhasználói adatbázis érvényes maradjon. Szüksége lesz egy, a WindowsHelloLogin.Models névtérre hivatkozó utasításra.

    using WindowsHelloLogin.Models;
    
    /// <summary>
    /// Function to be called when user requests deleting their account.
    /// Checks the KeyCredentialManager to see if there is a Windows Hello
    /// account for the current user.
    /// It then deletes the local key associated with the account.
    /// </summary>
    public static async void RemoveWindowsHelloAccountAsync(Account account)
    {
        // Open the account with Windows Hello
        KeyCredentialRetrievalResult keyOpenResult = await KeyCredentialManager.OpenAsync(account.Username);
    
        if (keyOpenResult.Status == KeyCredentialStatus.Success)
        {
            // In the real world you would send key information to server to unregister
            //for example, RemoveWindowsHelloAccountOnServer(account);
        }
    
        // Then delete the account from the machine's list of Windows Hello accounts
        await KeyCredentialManager.DeleteAsync(account.Username);
    }
    
  • Lépjen vissza Welcome.xaml.cs, és bontsa ki a RemoveWindowsHelloAccountAsync hívását kezdeményező sort.

    private void Button_Forget_User_Click(object sender, RoutedEventArgs e)
    {
        // Remove it from Windows Hello
        WindowsHelloHelper.RemoveWindowsHelloAccountAsync(_activeAccount);
    
        // Remove it from the local accounts list and re-save the updated list
        AccountHelper.RemoveAccount(_activeAccount);
    
        Debug.WriteLine($"User {_activeAccount.Username} deleted.");
    }
    
  • A SignInWindowsHelloAsync metódusban (Login.xaml.cs), amikor a CreateWindowsHelloKeyAsync sikeresen létrejön, a rendszer az Üdvözlés oldalra kell, hogy navigáljon, és adja át a Fiókot.

    private async void SignInWindowsHelloAsync()
    {
        if (AccountHelper.ValidateAccountCredentials(UsernameTextBox.Text))
        {
            // Create and add a new local account
            _account = AccountHelper.AddAccount(UsernameTextBox.Text);
            Debug.WriteLine("Successfully signed in with traditional credentials and created local account instance!");
    
            if (await WindowsHelloHelper.CreateWindowsHelloKeyAsync(UsernameTextBox.Text))
            {
                Debug.WriteLine("Successfully signed in with Windows Hello!");
                Frame.Navigate(typeof(Welcome), _account);
            }
        }
        else
        {
            ErrorMessage.Text = "Invalid Credentials";
        }
    }
    
  • Hozza létre és futtassa az alkalmazást. Jelentkezzen be a "sampleUsername" névvel, és kattintson a Bejelentkezés gombra. Adja meg a PIN-kódot, és ha sikeres, az üdvözlőképernyőre kell navigálnia. Kattintson a Felhasználó elfelejtése elemre, majd figyelje a Visual Studio Output ablakát, hogy a felhasználó törölve lett-e. Figyelje meg, hogy a felhasználó törlésekor az üdvözlőoldalon marad. Létre kell hoznia egy felhasználói kijelölési lapot, amelyen az alkalmazás navigálhat.

    A Windows Hello üdvözlőképernyőjének képernyőképe

  • A Nézetek mappában hozzon létre egy új üres lapot "UserSelection.xaml" néven, és adja hozzá a következő XAML-t a felhasználói felület meghatározásához. Ez a lap egy ListView-t tartalmaz, amely megjeleníti a helyi fiókok listájában szereplő összes felhasználót, és a Buttonbejelentkezési lapra lépve lehetővé teszi a felhasználó számára egy másik fiók hozzáadását.

    <Grid>
      <StackPanel>
        <TextBlock x:Name="Title" Text="Select a User" FontSize="36" Margin="4" TextAlignment="Center" HorizontalAlignment="Center"/>
    
        <ListView x:Name="UserListView" Margin="4" MaxHeight="200" MinWidth="250" Width="250" HorizontalAlignment="Center">
          <ListView.ItemTemplate>
            <DataTemplate>
              <Grid Background="DodgerBlue" Height="50" Width="250" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <TextBlock Text="{Binding Username}" HorizontalAlignment="Center" TextAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
              </Grid>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
    
        <Button x:Name="AddUserButton" Content="+" FontSize="36" Width="60" Click="AddUserButton_Click" HorizontalAlignment="Center"/>
      </StackPanel>
    </Grid>
    
  • A UserSelection.xaml.cs implementálja azt a Loaded metódust, amely a Bejelentkezési lapra lép, ha nincsenek fiókok a helyi listában. Implementáljon egy SelectionChanged eseményt a ListView számára, és egy Click eseményt a Button számára.

    using System.Diagnostics;
    using WindowsHelloLogin.Models;
    using WindowsHelloLogin.Utils;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class UserSelection : Page
        {
            public UserSelection()
            {
                InitializeComponent();
                Loaded += UserSelection_Loaded;
            }
    
            private void UserSelection_Loaded(object sender, RoutedEventArgs e)
            {
                if (AccountHelper.AccountList.Count == 0)
                {
                    // If there are no accounts, navigate to the Login page
                    Frame.Navigate(typeof(Login));
                }
    
    
                UserListView.ItemsSource = AccountHelper.AccountList;
                UserListView.SelectionChanged += UserSelectionChanged;
            }
    
            /// <summary>
            /// Function called when an account is selected in the list of accounts
            /// Navigates to the Login page and passes the chosen account
            /// </summary>
            private void UserSelectionChanged(object sender, RoutedEventArgs e)
            {
                if (((ListView)sender).SelectedValue != null)
                {
                    Account account = (Account)((ListView)sender).SelectedValue;
                    if (account != null)
                    {
                        Debug.WriteLine($"Account {account.Username} selected!");
                    }
                    Frame.Navigate(typeof(Login), account);
                }
            }
    
            /// <summary>
            /// Function called when the "+" button is clicked to add a new user.
            /// Navigates to the Login page with nothing filled out
            /// </summary>
            private void AddUserButton_Click(object sender, RoutedEventArgs e)
            {
                Frame.Navigate(typeof(Login));
            }
        }
    }
    
  • Az alkalmazásban van néhány hely, ahol a UserSelection oldalra szeretne navigálni. A MainPage.xaml.cs fájlban a Bejelentkezés lap helyett a UserSelection lapra kell navigálnia. Amíg a MainPage betöltési eseményében van, be kell töltenie a fióklistát, hogy a UserSelection oldal ellenőrizni tudja, hogy vannak-e fiókok. Ehhez módosítani kell a Loaded metódust aszinkron módon, és hozzá kell adni egy használati utasítást a WindowsHelloLogin.Utils névtérhez.

    using WindowsHelloLogin.Utils;
    
    private async void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        // Load the local account list before navigating to the UserSelection page
        await AccountHelper.LoadAccountListAsync();
        Frame.Navigate(typeof(UserSelection));
    }
    
  • Ezután az alkalmazásnak az üdvözlőlapon kell navigálnia a UserSelection lapra. Mindkét Click eseménynél vissza kell lépnie a UserSelection lapra.

    private void Button_Restart_Click(object sender, RoutedEventArgs e)
    {
        Frame.Navigate(typeof(UserSelection));
    }
    
    private void Button_Forget_User_Click(object sender, RoutedEventArgs e)
    {
        // Remove it from Windows Hello
        WindowsHelloHelper.RemoveWindowsHelloAccountAsync(_activeAccount);
    
        // Remove it from the local accounts list and re-save the updated list
        AccountHelper.RemoveAccount(_activeAccount);
    
        Debug.WriteLine($"User {_activeAccount.Username} deleted.");
    
        // Navigate back to UserSelection page.
        Frame.Navigate(typeof(UserSelection));
    }
    
  • A Bejelentkezési lapon a UserSelection lapon a listából kiválasztott fiókba való bejelentkezéshez kód szükséges. Ebben az OnNavigatedTo eseményben tárolja a navigáció során átadott fiókot. Először adjon hozzá egy új privát változót, amely azonosítja, hogy a fiók meglévő fiók-e. Az OnNavigatedTo eseményt ezután kezelje.

    namespace WindowsHelloLogin.Views
    {
        public sealed partial class Login : Page
        {
            private Account _account;
            private bool _isExistingAccount;
    
            public Login()
            {
                InitializeComponent();
            }
    
            /// <summary>
            /// Function called when this frame is navigated to.
            /// Checks to see if Windows Hello is available and if an account was passed in.
            /// If an account was passed in set the "_isExistingAccount" flag to true and set the _account.
            /// </summary>
            protected override async void OnNavigatedTo(NavigationEventArgs e)
            {
                // Check Windows Hello is set up and available on this machine
                if (await WindowsHelloHelper.WindowsHelloAvailableCheckAsync())
                {
                    if (e.Parameter != null)
                    {
                        _isExistingAccount = true;
                        // Set the account to the existing account being passed in
                        _account = (Account)e.Parameter;
                        UsernameTextBox.Text = _account.Username;
                        await SignInWindowsHelloAsync();
                    }
                }
                else
                {
                    // Windows Hello is not set up, so inform the user
                    WindowsHelloStatus.Background = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 50, 170, 207));
                    WindowsHelloStatusText.Text = $"Windows Hello is not set up!{Environment.NewLine}Please go to Windows Settings and set up a PIN to use it.";
                    LoginButton.IsEnabled = false;
                }
            }
        }
    }
    
  • A SignInWindowsHelloAsync metódust frissíteni kell a kiválasztott fiókba való bejelentkezéshez. A WindowsHelloHelper egy másik metódusra lesz szüksége a fiók Windows Hello való megnyitásához, mivel a fiókhoz már létrehozott egy fiókkulcsot. Implementálja az új módszert a WindowsHelloHelper.cs fájlban egy meglévő felhasználó bejelentkezéséhez a Windows Hello használatával. A kód minden részével kapcsolatos információkért olvassa el a kód megjegyzéseit.

    /// <summary>
    /// Attempts to sign a message using the account key on the system for the accountId passed.
    /// </summary>
    /// <returns>Boolean representing if creating the Windows Hello authentication message succeeded</returns>
    public static async Task<bool> GetWindowsHelloAuthenticationMessageAsync(Account account)
    {
        KeyCredentialRetrievalResult openKeyResult = await KeyCredentialManager.OpenAsync(account.Username);
        // Calling OpenAsync will allow the user access to what is available in the app and will not require user credentials again.
        // If you wanted to force the user to sign in again you can use the following:
        // var consentResult = await Windows.Security.Credentials.UI.UserConsentVerifier.RequestVerificationAsync(account.Username);
        // This will ask for the either the password of the currently signed in Microsoft Account or the PIN used for Windows Hello.
    
        if (openKeyResult.Status == KeyCredentialStatus.Success)
        {
            // If OpenAsync has succeeded, the next thing to think about is whether the client application requires access to backend services.
            // If it does here you would request a challenge from the server. The client would sign this challenge and the server
            // would check the signed challenge. If it is correct, it would allow the user access to the backend.
            // You would likely make a new method called RequestSignAsync to handle all this.
            // For example, RequestSignAsync(openKeyResult);
            // Refer to the second Windows Hello sample for information on how to do this.
    
            // For this sample, there is not concept of a server implemented so just return true.
            return true;
        }
        else if (openKeyResult.Status == KeyCredentialStatus.NotFound)
        {
            // If the account is not found at this stage. It could be one of two errors. 
            // 1. Windows Hello has been disabled
            // 2. Windows Hello has been disabled and re-enabled cause the Windows Hello Key to change.
            // Calling CreateWindowsHelloKeyAsync and passing through the account will attempt to replace the existing Windows Hello Key for that account.
            // If the error really is that Windows Hello is disabled then the CreateWindowsHelloKeyAsync method will output that error.
            if (await CreateWindowsHelloKeyAsync(account.Username))
            {
                // If the Hello Key was again successfully created, Windows Hello has just been reset.
                // Now that the Hello Key has been reset for the account retry sign in.
                return await GetWindowsHelloAuthenticationMessageAsync(account);
            }
        }
    
        // Can't use Windows Hello right now, try again later
        return false;
    }
    
  • Frissítse a SignInWindowsHelloAsync metódust Login.xaml.cs a meglévő fiók kezeléséhez. Ez az új metódust fogja használni a WindowsHelloHelper.cs. Ha sikeres, a fiók be lesz jelentkezve, és a felhasználó az üdvözlőoldalra navigál.

    private async Task SignInWindowsHelloAsync()
    {
        if (_isExistingAccount)
        {
            if (await WindowsHelloHelper.GetWindowsHelloAuthenticationMessageAsync(_account))
            {
                Frame.Navigate(typeof(Welcome), _account);
            }
        }
        else if (AccountHelper.ValidateAccountCredentials(UsernameTextBox.Text))
        {
            //Create and add a new local account
            _account = AccountHelper.AddAccount(UsernameTextBox.Text);
            Debug.WriteLine("Successfully signed in with traditional credentials and created local account instance!");
    
            if (await WindowsHelloHelper.CreateWindowsHelloKeyAsync(UsernameTextBox.Text))
            {
                Debug.WriteLine("Successfully signed in with Windows Hello!");
                Frame.Navigate(typeof(Welcome), _account);
            }
        }
        else
        {
            ErrorMessage.Text = "Invalid Credentials";
        }
    }
    
  • Hozza létre és futtassa az alkalmazást. Jelentkezzen be a "sampleUsername" névvel. Írja be a PIN-kódot, és ha sikeres, a rendszer az üdvözlőoldalra navigál. Kattintson vissza a felhasználói listára. Most már látnia kell egy felhasználót a listában. Ha erre kattint, a WindowsHello lehetővé teszi a bejelentkezést anélkül, hogy újra be kellene írnia a jelszavakat stb.

    A Windows Hello felhasználói lista képernyőképe

3. gyakorlat: Új Windows Hello felhasználó regisztrálása

Ebben a gyakorlatban létrehoz egy új oldalt, amely lehetővé teszi egy új fiók létrehozását a Windows Hellóval. Ez a bejelentkezési oldal működéséhez hasonlóan működik. A Login lap egy olyan meglévő felhasználóhoz van implementálva, aki Windows Hello szeretne használni. A WindowsHelloRegister lapon Windows Hello új felhasználó regisztrációja jön létre.

  • A Nézetek mappában hozzon létre egy új üres lapot "WindowsHelloRegister.xaml" néven. Az XAML-ben adja hozzá az alábbiakat a felhasználói felület beállításához. Az oldal felülete hasonló a Bejelentkezési laphoz.

    <Grid>
      <StackPanel>
        <TextBlock x:Name="Title" Text="Register New Windows Hello User" FontSize="24" Margin="4" TextAlignment="Center"/>
    
        <TextBlock x:Name="ErrorMessage" Text="" FontSize="20" Margin="4" Foreground="Red" TextAlignment="Center"/>
    
        <TextBlock Text="Enter your new username below" Margin="0,0,0,20"
                   TextWrapping="Wrap" Width="300"
                   TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
    
        <TextBox x:Name="UsernameTextBox" Margin="4" Width="250"/>
    
        <Button x:Name="RegisterButton" Content="Register" Background="DodgerBlue" Foreground="White"
                Click="RegisterButton_Click_Async" Width="80" HorizontalAlignment="Center" Margin="0,20"/>
    
        <Border x:Name="WindowsHelloStatus" Background="#22B14C" Margin="4" Height="100">
          <TextBlock x:Name="WindowsHelloStatusText" Text="Windows Hello is ready to use!" FontSize="20"
                     Margin="4" TextAlignment="Center" VerticalAlignment="Center"/>
        </Border>
      </StackPanel>
    </Grid>
    
  • A WindowsHelloRegister.xaml.cs kód mögötti fájlban implementáljon egy privát Account változót és egy eseményt Click a regisztrációs gombhoz. Ezzel hozzáad egy új helyi fiókot, és létrehoz egy Windows Hello kulcsot.

    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml;
    using WindowsHelloLogin.Models;
    using WindowsHelloLogin.Utils;
    
    namespace WindowsHelloLogin.Views
    {
        public sealed partial class WindowsHelloRegister : Page
        {
            private Account _account;
    
            public WindowsHelloRegister()
            {
                InitializeComponent();
            }
    
            private async void RegisterButton_Click_Async(object sender, RoutedEventArgs e)
            {
                ErrorMessage.Text = "";
    
                // In the real world, you would validate the entered credentials and information before 
                // allowing a user to register a new account. 
                // For this sample, we'll skip that step and just register an account if the username is not null.
    
                if (!string.IsNullOrEmpty(UsernameTextBox.Text))
                {
                    // Register a new account
                    _account = AccountHelper.AddAccount(UsernameTextBox.Text);
                    // Register new account with Windows Hello
                    await WindowsHelloHelper.CreateWindowsHelloKeyAsync(_account.Username);
                    // Navigate to the Welcome page. 
                    Frame.Navigate(typeof(Welcome), _account);
                }
                else
                {
                    ErrorMessage.Text = "Please enter a username";
                }
            }
        }
    }
    
  • Erre a lapra kell navigálnia a bejelentkezési lapról, amikor a regisztrációra kattint.

    private void RegisterButtonTextBlock_OnPointerPressed(object sender, PointerRoutedEventArgs e)
    {
        ErrorMessage.Text = "";
        Frame.Navigate(typeof(WindowsHelloRegister));
    }
    
  • Hozza létre és futtassa az alkalmazást. Próbáljon meg új felhasználót regisztrálni. Ezután térjen vissza a felhasználói listához, és ellenőrizze, hogy kiválaszthatja-e a felhasználót, és bejelentkezhet-e.

    A Windows Hello új felhasználó regisztrálási oldal képernyőképe

Ebben a laborban elsajátította azokat az alapvető készségeket, amelyek szükségesek ahhoz, hogy az új Windows Hello API-val hitelesítse a meglévő felhasználókat, és új felhasználók számára hozzon létre fiókokat. Ezzel az új tudással megszüntetheti annak szükségességét, hogy a felhasználók megjegyezzenek az alkalmazáshoz tartozó jelszavakat, de továbbra is biztosak lehetnek abban, hogy az alkalmazások felhasználói hitelesítéssel védettek maradnak. A Windows Windows Hello új hitelesítési technológiájával támogatja a biometrikus bejelentkezési lehetőségeket.