Sdílet prostřednictvím


Vytvoření přihlašovací aplikace Windows Hello

Toto je první část kompletního návodu, jak vytvořit zabalenou aplikaci pro Windows, která používá Windows Hello jako alternativu k tradičním systémům ověřování uživatelských jmen a hesel. V tomto případě se jedná o aplikaci WinUI, ale stejný přístup je možné použít s libovolnou zabalenou aplikací pro Windows, včetně aplikací WPF a Windows Forms. Aplikace používá pro přihlášení uživatelské jméno a pro každý účet vytvoří klíč Hello. Tyto účty budou chráněny PIN kódem nastaveným v nastavení Windows v konfiguraci Windows Hello.

Tento názorný postup je rozdělený do dvou částí: sestavení aplikace a připojení back-endové služby. Až tento článek dokončíte, pokračujte k části 2: služba přihlášení Windows Hello.

Než začnete, měli byste si přečíst přehled Windows Hello, abyste pochopili, jak Funguje Windows Hello.

Začínáme

K sestavení tohoto projektu budete potřebovat zkušenosti s jazykem C# a XAML. Budete také muset používat Visual Studio 2022 na počítači s Windows 10 nebo Windows 11. Úplné pokyny k nastavení vývojového prostředí najdete v tématu Zahájení vývoje aplikací pro Windows .

  • V prostředí Visual Studio vyberte Soubor>Nový>Projekt.
  • V rozevíracích filtrech dialogu Nový projekt vyberte C#/C++, Windows a WinUI.
  • Zvolte Prázdnou aplikaci, balíček (WinUI 3 na Desktopu) a pojmenujte ji "WindowsHelloLogin".
  • Sestavte a spusťte novou aplikaci (F5), mělo by se na obrazovce zobrazit prázdné okno. Zavřete aplikaci.

Snímek obrazovky s novou aplikací pro přihlášení k Windows Hello spuštěnou poprvé

Cvičení 1: Přihlášení pomocí Windows Hello

V tomto cvičení se dozvíte, jak zkontrolovat, jestli je na počítači nastavená služba Windows Hello a jak se přihlásit k účtu pomocí Windows Hello.

  • V novém projektu vytvořte v řešení novou složku s názvem Zobrazení. Tato složka bude obsahovat stránky, na které přejdete v této ukázce. Klikněte pravým tlačítkem myši na projekt v Průzkumníku řešení, vyberte Přidat>novou složku a potom složku přejmenujte na Zobrazení.

    Snímek obrazovky s přidáním nové složky s názvem Zobrazení do projektu přihlášení k Windows Hello

  • Otevřete MainWindow.xaml a nahraďte Window obsah prázdným StackPanel nebo Grid ovládacím prvku. Budeme implementovat navigaci stránky a budeme přecházet na novou stránku, když se načte MainWindow, takže nebude potřeba žádný obsah v MainWindow.

  • Title Přidejte vlastnost do MainWindow v XAML. Atribut by měl vypadat takto: Title="Windows Hello Login".

  • Odeberte z MainWindow.xaml.cs obslužnou rutinu události myButton_Click, aby nedošlo k chybám při kompilaci. Tato obslužná rutina události není pro tuto ukázku nutná.

  • Klikněte pravým tlačítkem myši na novou složku Zobrazení , vyberte Přidat>novou položku a vyberte šablonu Prázdná stránka . Pojmenujte tuto stránku "MainPage.xaml".

    Snímek obrazovky s přidáním nové prázdné stránky do projektu přihlášení k Windows Hello

  • Otevřete soubor App.xaml.cs a aktualizujte obslužnou rutinu OnLaunched tak, aby implementovala navigaci na stránce aplikace. Budete také muset přidat metodu obslužné rutiny RootFrame_NavigationFailed, která řeší všechny chyby, ke kterým dochází při načítání stránek.

    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}");
    }
    
  • K vyřešení chyb kompilace v kódu budete také muset přidat čtyři příkazy using na začátek souboru App.xaml.cs.

    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Navigation;
    using System;
    using WindowsHelloLogin.Views;
    
  • Klikněte pravým tlačítkem myši na novou složku Zobrazení , vyberte Přidat>novou položku a vyberte šablonu Prázdná stránka . Pojmenujte tuto stránku Login.xaml.

  • Chcete-li definovat uživatelské rozhraní pro novou přihlašovací stránku, přidejte následující XAML. Tento XAML definuje StackPanel k zarovnání následujících podřízených prvků:

    • Jeden TextBlock, který bude obsahovat název.

    • A TextBlock pro chybové zprávy.

    • A TextBox pro uživatelské jméno, které se má zadat.

    • Navigujte na Button stránku registrace.

    • A TextBlock, která bude obsahovat stav Windows Hello.

    • Pro vysvětlení přihlašovací stránky TextBlock, protože zatím není žádný backend ani nakonfigurovaní uživatelé.

      <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>
      
  • Aby bylo možné řešení sestavit, je nutné do souboru code-behind přidat několik metod. Stiskněte klávesu F7 nebo pomocí Průzkumníka řešení upravte soubor Login.xaml.cs. Přidejte následující dvě metody událostí pro zpracování událostí Přihlášení a Registrace. Prozatím tyto metody nastaví ErrorMessage.Text na prázdný řetězec. Nezapomeňte zahrnout následující příkazy using. Budou potřeba pro další kroky.

    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 = "";
            }
        }
    }
    
  • Chcete-li vykreslit přihlašovací stránku, upravte kód MainPage tak, aby při načtení MainPage přejděte na přihlašovací stránku. Otevřete soubor MainPage.xaml.cs. V Průzkumníku řešení poklikejte na MainPage.xaml.cs. Pokud nemůžete toto najít, klikněte na malou šipku vedle MainPage.xaml, abyste zobrazili soubor s kódem. Vytvořte obslužnou metodu Načtené události, která přejde na přihlašovací stránku.

    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));
            }
        }
    }
    
  • Na přihlašovací stránce musíte událost zpracovat OnNavigatedTo , abyste ověřili, jestli je na aktuálním počítači k dispozici Windows Hello. V Login.xaml.cs implementujte následující kód. Všimněte si, že objekt WindowsHelloHelper označuje, že došlo k chybě. Je to proto, že jsme ještě nevytvořili tuto pomocnou třídu.

    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;
            }
        }
    }
    
  • Chcete-li vytvořit Třídu WindowsHelloHelper, klepněte pravým tlačítkem myši na projekt WindowsHelloLogin a klepněte na tlačítko Přidat>novou složku. Pojmenujte tuto složku Utils.

  • Klikněte pravým tlačítkem na složku Utils a vyberte Přidat>třídu. Pojmenujte tuto novou třídu "WindowsHelloHelper.cs".

    Snímek obrazovky vytvoření pomocné třídy přihlášení Windows Hello

  • Změňte obor třídy WindowsHelloHelper tak, aby byl public static, pak přidejte následující metodu, která informuje uživatele, pokud je Windows Hello připraven k použití nebo ne. Budete muset přidat požadované obory názvů.

    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;
            }
        }
    }
    
  • V Login.xaml.cs přidejte odkaz na WindowsHelloLogin.Utils obor názvů. Tím se vyřeší chyba v OnNavigatedTo metodě.

    using WindowsHelloLogin.Utils;
    
  • Sestavte a spusťte aplikaci. Přejdete na přihlašovací stránku a banner Windows Hello vám oznámí, jestli je Windows Hello připravený k použití. Na vašem počítači by se měl zobrazit zelený nebo modrý nápis označující stav Windows Hello.

    Snímek obrazovky s přihlašovací obrazovkou Windows Hello s připraveným stavem

  • Další věcí, kterou musíte udělat, je vytvoření logiky pro přihlášení. Vytvořte novou složku v projektu s názvem Modely.

  • Ve složce Models vytvořte novou třídu s názvem "Account.cs". Tato třída bude fungovat jako model účtu. Protože se jedná o ukázkový projekt, bude obsahovat pouze uživatelské jméno. Změňte obor třídy na public a přidejte Username vlastnost.

    namespace WindowsHelloLogin.Models
    {
        public class Account
        {
            public string Username { get; set; }
        }
    }
    
  • Aplikace potřebuje způsob, jak zpracovávat účty. Pro účely tohoto praktického cvičení se seznam uživatelů ukládá a načítá místně, protože neexistuje žádný server ani databáze. Klikněte pravým tlačítkem na složku Utils a přidejte novou třídu s názvem "AccountHelper.cs". Změňte obor třídy tak, aby byl public static. AccountHelper je statická třída, která obsahuje všechny nezbytné metody pro uložení a načtení seznamu účtů místně. Ukládání a načítání funguje pomocí XmlSerializeru. Musíte si také zapamatovat soubor, který jste uložili a kam jste ho uložili.

    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>;
            }
        }
    }
    
  • Dále implementujte způsob, jak přidat a odebrat účet z místního seznamu účtů. Každá z těchto akcí uloží seznam. Poslední metodou, kterou budete potřebovat pro toto praktické cvičení, je metoda ověřování. Vzhledem k tomu, že neexistuje žádný autorizační server ani databáze uživatelů, ověří se to u jednoho uživatele, který je pevně zakódovaný. Tyto metody by měly být přidány do třídy AccountHelper .

    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;
    }
    
  • Další věcí, kterou musíte udělat, je zpracování žádosti o přihlášení od uživatele. V Login.xaml.cs vytvořte novou privátní proměnnou, která bude obsahovat aktuální přihlášení k účtu. Pak přidejte novou metodu s názvem SignInWindowsHelloAsync. Tím ověříte přihlašovací údaje účtu pomocí metody AccountHelper.ValidateAccountCredentials . Tato metoda vrátí logickou hodnotu, pokud zadané uživatelské jméno je stejné jako pevně zakódovaná řetězcová hodnota, kterou jste nakonfigurovali v předchozím kroku. Pevně zakódovaná hodnota pro tuto ukázku je 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";
                }
            }
        }
    }
    
  • Možná jste si všimli okomentovaného kódu, který odkazoval na metodu ve WindowsHelloHelper. V WindowsHelloHelper.cs přidejte novou metodu s názvem CreateWindowsHelloKeyAsync. Tato metoda používá rozhraní API Windows Hello v keyCredentialManager. Volání RequestCreateAsync vytvoří klíč Windows Hello, který je specifický pro accountId a místní počítač. Pokud vás zajímá implementace tohoto příkazu v reálném světě, mějte prosím na paměti komentáře v příkazu switch.

    /// <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;
    }
    
  • Teď, když jste vytvořili metodu CreateWindowsHelloKeyAsync, vraťte se do souboru Login.xaml.cs a odkomentujte kód uvnitř metody SignInWindowsHelloAsync.

    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";
        }
    }
    
  • Sestavte a spusťte aplikaci. Přejdete na přihlašovací stránku. Zadejte uživatelské jméno jako sampleUsername a klikněte na přihlásit. Zobrazí se výzva k zadání kódu PIN ve Windows Hello. Po správném zadání kódu PIN bude moct metoda CreateWindowsHelloKeyAsync vytvořit klíč Windows Hello. Sledujte okna výstupu a podívejte se, jestli se zobrazují zprávy označující úspěch.

    Snímek obrazovky výzvy k zadání PIN kódu Windows Hello

Cvičení 2: Úvodní stránky a stránky výběru uživatelů

V tomto cvičení budete pokračovat z předchozího cvičení. Když se uživatel úspěšně přihlásí, měl by být přesměrován na úvodní stránku, kde se může odhlásit nebo odstranit svůj účet. Když Windows Hello vytvoří klíč pro každý počítač, můžete vytvořit obrazovku výběru uživatele, která zobrazí všechny uživatele přihlášené na daném počítači. Uživatel pak může vybrat jeden z těchto účtů a přejít přímo na úvodní obrazovku, aniž by musel znovu zadat heslo, protože už byl ověřený pro přístup k počítači.

  • Do složky Zobrazení přidejte novou prázdnou stránku s názvem Welcome.xaml. Přidejte následující xaml pro dokončení uživatelského rozhraní stránky. Zobrazí se název, přihlášené uživatelské jméno a dvě tlačítka. Jedno z tlačítek přejde zpět do seznamu uživatelů (které vytvoříte později) a druhé tlačítko zpracuje zapomenutí tohoto uživatele.

    <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>
    
  • Do souboru kódu za stránkou Welcome.xaml.cs přidejte novou soukromou proměnnou, která bude uchovávat přihlášený účet. Budete muset implementovat metodu k přepsání události OnNavigateTo, což uloží účet, který je předán na stránku Welcome. Budete také muset implementovat Click událost pro dvě tlačítka definovaná v XAML. Budete muset přidat příkazy using pro obory názvů WindowsHelloLogin.Models a WindowsHelloLogin.Utils.

    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.");
            }
        }
    }
    
  • Možná jste si v obslužné rutině události všimli okomentovaného řádku Button_Forget_User_Click. Účet se odebere z místního seznamu, ale v současné době není možné odebrat z Windows Hello. V WindowsHelloHelper.cs musíte implementovat novou metodu, která bude zpracovávat odebrání uživatele Windows Hello. Tato metoda použije jiná rozhraní API Windows Hello k otevření a odstranění účtu. Když v reálném světě odstraníte účet, měl by být server nebo databáze upozorněn, aby uživatelská databáze zůstala platná. Budete potřebovat příkaz using, který odkazuje na WindowsHelloLogin.Models obor názvů.

    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);
    }
    
  • Zpět v Welcome.xaml.cs odkomentujte řádek, který volá RemoveWindowsHelloAccountAsync.

    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.");
    }
    
  • Metoda SignInWindowsHelloAsync (v Login.xaml.cs) by měla po úspěšném dokončení CreateWindowsHelloKeyAsync přejít na stránku Vítejte a předat jako parametr Account.

    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";
        }
    }
    
  • Sestavte a spusťte aplikaci. Přihlaste se pomocí sampleUsername a klikněte na Přihlásit. Zadejte svůj PIN kód a pokud budete úspěšní, měli byste přejít na úvodní obrazovku. Zkuste kliknout na Zapomenout uživatele a sledovat okno Výstup sady Visual Studio, abyste zjistili, jestli byl uživatel odstraněn. Všimněte si, že po odstranění uživatele zůstanete na úvodní stránce. Budete muset vytvořit stránku pro výběr uživatele, na kterou může aplikace přecházet.

    Snímek obrazovky úvodní obrazovky Windows Hello

  • Ve složce Zobrazení vytvořte novou prázdnou stránku s názvem UserSelection.xaml a přidejte následující XAML pro definování uživatelského rozhraní. Tato stránka bude obsahovat ListView , který zobrazí všechny uživatele v seznamu místních účtů a Button který přejde na přihlašovací stránku, aby uživatel mohl přidat další účet.

    <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>
    
  • V UserSelection.xaml.cs implementujte metodu Loaded , která přejde na přihlašovací stránku, pokud v místním seznamu nejsou žádné účty. Také implementujte SelectionChanged událost pro ListView a Click událost pro Button.

    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));
            }
        }
    }
    
  • V aplikaci je několik míst, kde chcete přejít na stránku UserSelection . V MainPage.xaml.cs byste měli místo přihlašovací stránky přejít na stránku UserSelection. Když jste v události načítání v MainPage, budete muset načíst seznam účtů, aby stránka UserSelection mohla zkontrolovat, jestli existují nějaké účty. To bude vyžadovat změnu Loaded metody tak, aby byla asynchronní, a také přidání příkazu using pro WindowsHelloLogin.Utils obor názvů.

    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));
    }
    
  • V dalším kroku bude aplikace muset přejít na stránku UserSelection z úvodní stránky. V obou Click událostech byste měli přejít zpět na stránku UserSelection .

    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));
    }
    
  • Na přihlašovací stránce potřebujete kód pro přihlášení k účtu vybranému ze seznamu na stránce UserSelection . Při události OnNavigatedTo uložte účet, který byl předán během navigace. Začněte přidáním nové privátní proměnné, která identifikuje, jestli je účet existujícím účtem. Pak zpracujte OnNavigatedTo událost.

    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;
                }
            }
        }
    }
    
  • Pro přihlášení k vybranému účtu bude potřeba aktualizovat metodu SignInWindowsHelloAsync . WindowsHelloHelper bude potřebovat další metodu pro otevření účtu s Windows Hello, protože účet už má vytvořený klíč účtu. Implementujte novou metodu v WindowsHelloHelper.cs pro přihlášení existujícího uživatele pomocí Windows Hello. Informace o každé části kódu najdete v komentářích ke kódu.

    /// <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;
    }
    
  • Aktualizujte metodu SignInWindowsHelloAsync v Login.xaml.cs pro zpracování existujícího účtu. Použije se nová metoda v WindowsHelloHelper.cs. Pokud bude účet úspěšný, přihlásí se a uživatel přejde na úvodní stránku.

    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";
        }
    }
    
  • Sestavte a spusťte aplikaci. Přihlaste se pomocí sampleUsername. Zadejte svůj PIN kód a pokud budete úspěšní, přejdete na úvodní stránku. Klikněte zpět na seznam uživatelů. V seznamu by se teď měl zobrazit uživatel. Pokud na to kliknete, WindowsHello vám umožní se znovu přihlásit, aniž byste museli znovu zadávat hesla atd.

    Snímek obrazovky se seznamem uživatelů pro výběr Windows Hello

Cvičení 3: Registrace nového uživatele Windows Hello

V tomto cvičení vytvoříte novou stránku, která může vytvořit nový účet pomocí Windows Hello. Funguje to podobně jako přihlašovací stránka . Přihlašovací stránka se implementuje pro existujícího uživatele, který migruje na používání Windows Hello. Stránka WindowsHelloRegister vytvoří registraci Windows Hello pro nového uživatele.

  • Ve složce Zobrazení vytvořte novou prázdnou stránku s názvem WindowsHelloRegister.xaml. V kódu XAML přidejte následující kód pro nastavení uživatelského rozhraní. Rozhraní na této stránce je podobné přihlašovací stránce.

    <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>
    
  • V souboru WindowsHelloRegister.xaml.cs v kódu implementujte privátní proměnnou Account a událost Click pro registrační tlačítko. Tím se přidá nový místní účet a vytvoří se klíč Windows Hello.

    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";
                }
            }
        }
    }
    
  • Po kliknutí na registraci je nutné přejít na tuto stránku z přihlašovací stránky.

    private void RegisterButtonTextBlock_OnPointerPressed(object sender, PointerRoutedEventArgs e)
    {
        ErrorMessage.Text = "";
        Frame.Navigate(typeof(WindowsHelloRegister));
    }
    
  • Sestavte a spusťte aplikaci. Pokuste se zaregistrovat nového uživatele. Pak se vraťte do seznamu uživatelů a ověřte, že můžete vybrat daného uživatele a přihlásit se.

    Snímek obrazovky se stránkou registrace nového uživatele windows Hello

V tomto cvičení jste se naučili základní dovednosti potřebné k použití nového rozhraní API Windows Hello k ověřování stávajících uživatelů a vytváření účtů pro nové uživatele. S těmito novými znalostmi můžete začít odebírat potřebu uživatelů pamatovat si hesla pro vaši aplikaci, ale zůstat přesvědčeni, že vaše aplikace zůstanou chráněné ověřováním uživatelů. Systém Windows používá novou ověřovací technologii Windows Hello k podpoře možností přihlášení pomocí biometrických údajů.