Cvičení – přidání ověřování uživatelů

Dokončeno

Webová aplikace nákupního seznamu potřebuje ověření uživatele. V tomto cvičení implementujete přihlášení a odhlášení do aplikace a zobrazíte aktuální stav přihlášení uživatele.

V tomto cvičení provedete následující kroky:

  1. Nainstalujte rozhraní příkazového řádku Static Web Apps pro místní vývoj.
  2. Spusťte aplikaci a rozhraní API místně s emulací místního ověřování.
  3. Přidání přihlašovacích tlačítek pro více zprostředkovatelů ověřování
  4. Pokud je uživatel přihlášený, přidejte tlačítko odhlášení.
  5. Zobrazí stav přihlášení uživatele.
  6. Místně otestujte pracovní postup ověřování.
  7. Nasaďte aktualizovanou aplikaci.

Příprava na místní vývoj

Static Web Apps CLI, označované také jako SWA CLI, je místní vývojový nástroj, který umožňuje místní spuštění webové aplikace a rozhraní API a emulace ověřovacích a autorizačních serverů.

  1. Na počítači si otevřete terminál.

  2. Nainstalujte rozhraní příkazového řádku SWA spuštěním následujícího příkazu.

    npm install -g @azure/static-web-apps-cli
    

Místní spuštění aplikace

Teď spusťte aplikaci a rozhraní API místně s vývojovým serverem. Díky tomu uvidíte a otestujete změny, jak je uděláte v kódu.

  1. Otevřete projekt v editoru Visual Studio Code.

  2. V editoru Visual Studio Code otevřete paletu příkazů stisknutím klávesy F1.

  3. Zadejte a vyberte Terminál: Vytvořit nový integrovaný terminál.

  4. Přejděte do složky preferované front-endové architektury následujícím způsobem:

    cd angular-app
    
    cd react-app
    
    cd svelte-app
    
    cd vue-app
    
  5. Spusťte front-endovou klientskou aplikaci pomocí vývojového serveru.

    npm start
    
    npm start
    
    npm run dev
    
    npm run serve
    

    Nechte tento server spuštěný na pozadí. Teď spusťte emulátor rozhraní API a ověřovacího serveru pomocí rozhraní příkazového řádku SWA.

  6. V editoru Visual Studio Code otevřete paletu příkazů stisknutím klávesy F1.

  7. Zadejte a vyberte Terminál: Vytvořit nový integrovaný terminál.

  8. Spuštěním následujícího příkazu spusťte rozhraní příkazového řádku SWA:

    swa start http://localhost:4200 --api-location ./api
    
    swa start http://localhost:3000 --api-location ./api
    
    swa start http://localhost:5000 --api-location ./api
    
    swa start http://localhost:8080 --api-location ./api
    
  9. Přejděte na http://localhost:4280.

Poslední port používaný rozhraním příkazového řádku SWA se liší od portu, který jste viděli dříve, protože k předávání požadavků třem různým komponentám používá reverzní proxy server:

  • Váš vývojový server architektury
  • Emulátor ověřování a autorizace
  • Rozhraní API hostované modulem runtime Služby Functions

Snímek obrazovky architektury rozhraní příkazového řádku Static Web Apps

Nechte aplikaci běžet při úpravě kódu.

Získání stavu přihlášení uživatele

Nejprve potřebujete získat přístup ke stavu přihlášení uživatele tak, že v klientovi zadáte dotaz /.auth/me .

  1. Vytvořte soubor angular-app/src/app/core/models/user-info.ts a přidejte následující kód, který představuje rozhraní pro informace o uživateli.

    export interface UserInfo {
      identityProvider: string;
      userId: string;
      userDetails: string;
      userRoles: string[];
    }
    
  2. Upravte soubor angular-app/src/app/core/components/nav.component.tsa přidejte do třídy následující metodu NavComponent .

    async getUserInfo() {
      try {
        const response = await fetch('/.auth/me');
        const payload = await response.json();
        const { clientPrincipal } = payload;
        return clientPrincipal;
      } catch (error) {
        console.error('No profile could be found');
        return undefined;
      }
    }
    
  3. Vytvořte novou vlastnost třídy userInfoa uložte výsledek asynchronní funkce getUserInfo() při inicializaci komponenty. OnInit Implementujte rozhraní a aktualizujte příkazy importu pro import OnInit a UserInfo. Tento kód načte informace o uživateli při inicializaci komponenty.

    import { Component, OnInit } from '@angular/core';
    import { UserInfo } from '../model/user-info';
    
    export class NavComponent implements OnInit {
      userInfo: UserInfo;
    
      async ngOnInit() {
        this.userInfo = await this.getUserInfo();
      }
      // ...
    }
    
  1. Upravte soubor react-app/src/components/NavBar.jsa do horní části funkce přidejte následující kód. Tento kód načte informace o uživateli, když komponenta načte a uloží je do stavu.

    import React, { useState, useEffect } from 'react';
    import { NavLink } from 'react-router-dom';
    
    const NavBar = (props) => {
      const [userInfo, setUserInfo] = useState();
    
      useEffect(() => {
        (async () => {
          setUserInfo(await getUserInfo());
        })();
      }, []);
    
      async function getUserInfo() {
        try {
          const response = await fetch('/.auth/me');
          const payload = await response.json();
          const { clientPrincipal } = payload;
          return clientPrincipal;
        } catch (error) {
          console.error('No profile could be found');
          return undefined;
        }
      }
    
      return (
      // ...
    
  1. Upravte soubor svelte-app/src/components/NavBar.sveltea do části skriptu přidejte následující kód. Tento kód načte informace o uživateli při načtení komponenty.

    import { onMount } from 'svelte';
    
    let userInfo = undefined;
    
    onMount(async () => (userInfo = await getUserInfo()));
    
    async function getUserInfo() {
      try {
        const response = await fetch('/.auth/me');
        const payload = await response.json();
        const { clientPrincipal } = payload;
        return clientPrincipal;
      } catch (error) {
        console.error('No profile could be found');
        return undefined;
      }
    }
    
  1. Upravte soubor vue-app/src/components/nav-bar.vuea přidejte userInfo ho do datového objektu.

     data() {
       return {
         userInfo: {
           type: Object,
           default() {},
         },
       };
     },
    
  2. Přidejte metodu getUserInfo() do oddílu metod .

    methods: {
      async getUserInfo() {
        try {
          const response = await fetch('/.auth/me');
          const payload = await response.json();
          const { clientPrincipal } = payload;
          return clientPrincipal;
        } catch (error) {
          console.error('No profile could be found');
          return undefined;
        }
      },
    },
    
  3. created Přidejte do komponenty háček životního cyklu.

    async created() {
      this.userInfo = await this.getUserInfo();
    },
    

    Po vytvoření komponenty se informace o uživateli načítají automaticky.

Přidání tlačítek pro přihlášení a odhlášení

Informace o uživateli budou, undefined pokud nejsou přihlášené, takže změny se prozatím nezobrazí. Je čas přidat přihlašovací tlačítka pro různé poskytovatele.

  1. Upravte soubor angular-app/src/app/core/components/nav.component.ts a přidejte do třídy seznam zprostředkovatelů NavComponent .

    providers = ['x', 'github', 'aad'];
    
  2. Přidejte následující redirect vlastnost pro zachycení aktuální adresy URL pro přesměrování po přihlášení.

    redirect = window.location.pathname;
    
  3. Přidejte následující kód do šablony za první </nav> prvek, aby se zobrazila tlačítka pro přihlášení a odhlášení.

    <nav class="menu auth">
      <p class="menu-label">Auth</p>
      <div class="menu-list auth">
        <ng-container *ngIf="!userInfo; else logout">
          <ng-container *ngFor="let provider of providers">
            <a href="/.auth/login/{{provider}}?post_login_redirect_uri={{redirect}}">{{provider}}</a>
          </ng-container>
        </ng-container>
        <ng-template #logout>
          <a href="/.auth/logout?post_logout_redirect_uri={{redirect}}">Logout</a>
        </ng-template>
      </div>
    </nav>
    

    Pokud uživatel není přihlášený, zobrazí se přihlašovací tlačítko pro každého zprostředkovatele. Každé tlačítko odkazuje na /.auth/login/<AUTH_PROVIDER>a nastaví adresu URL přesměrování na aktuální stránku.

    V opačném případě, pokud je uživatel již přihlášen, zobrazí tlačítko odhlášení odkazy /.auth/logoutna a také nastaví adresu URL přesměrování na aktuální stránku.

Teď byste měli vidět tuto webovou stránku v prohlížeči.

Snímek obrazovky webové aplikace Angular s tlačítky pro přihlášení

  1. Upravte soubor react-app/src/components/NavBar.js a přidejte seznam zprostředkovatelů v horní části funkce.

    const providers = ['x', 'github', 'aad'];
    
  2. Přidejte následující redirect proměnnou pod první proměnnou, která zachytí aktuální adresu URL pro přesměrování přihlášení po přihlášení.

    const redirect = window.location.pathname;
    
  3. Přidejte následující kód do šablony JSX za první </nav> prvek pro zobrazení tlačítek pro přihlášení a odhlášení.

    <nav className="menu auth">
      <p className="menu-label">Auth</p>
      <div className="menu-list auth">
        {!userInfo &&
          providers.map((provider) => (
            <a key={provider} href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}>
              {provider}
            </a>
          ))}
        {userInfo && <a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}>Logout</a>}
      </div>
    </nav>
    

    Pokud uživatel není přihlášený, zobrazí se přihlašovací tlačítko pro každého zprostředkovatele. Každé tlačítko odkazuje na /.auth/login/<AUTH_PROVIDER>a nastaví adresu URL přesměrování na aktuální stránku.

    Jinak pokud je uživatel již přihlášený, zobrazí se tlačítko odhlášení, na které odkazuje /.auth/logout, a také nastaví adresu URL přesměrování na aktuální stránku.

Teď byste měli vidět tuto webovou stránku v prohlížeči.

Snímek obrazovky webové aplikace React s přihlašovacími tlačítky

  1. Upravte soubor svelte-app/src/components/NavBar.svelte a přidejte seznam zprostředkovatelů v horní části skriptu.

    const providers = ['x', 'github', 'aad'];
    
  2. Přidejte následující redirect proměnnou pod první proměnnou, která zachytí aktuální adresu URL pro přesměrování přihlášení po přihlášení.

    const redirect = window.location.pathname;
    
  3. Přidejte následující kód do šablony za první </nav> prvek, aby se zobrazila tlačítka pro přihlášení a odhlášení.

     <nav class="menu auth">
       <p class="menu-label">Auth</p>
       <div class="menu-list auth">
         {#if !userInfo}
           {#each providers as provider (provider)}
             <a href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}>
               {provider}
             </a>
           {/each}
         {/if}
         {#if userInfo}
           <a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}>
             Logout
           </a>
         {/if}
       </div>
     </nav>
    

    Pokud uživatel není přihlášený, zobrazí se přihlašovací tlačítko pro každého zprostředkovatele. Každé tlačítko odkazuje na /.auth/login/<AUTH_PROVIDER>a nastaví adresu URL přesměrování na aktuální stránku.

    Jinak pokud je uživatel již přihlášený, zobrazí se tlačítko odhlášení, na které odkazuje /.auth/logout, a také nastaví adresu URL přesměrování na aktuální stránku.

Teď byste měli vidět tuto webovou stránku v prohlížeči.

Snímek obrazovky webové aplikace Svelte s tlačítky pro přihlášení

  1. Upravte soubor vue-app/src/components/nav-bar.vuea přidejte do datového objektu seznam zprostředkovatelů.

     providers: ['x', 'github', 'aad'],
    
  2. Přidejte následujícíredirect vlastnost pro zachycení aktuální adresy URL pro přesměrování po přihlášení.

     redirect: window.location.pathname,
    
  3. Přidejte následující kód do šablony za první </nav> prvek, aby se zobrazila tlačítka pro přihlášení a odhlášení.

    <nav class="menu auth">
      <p class="menu-label">Auth</p>
      <div class="menu-list auth">
        <template v-if="!userInfo">
          <template v-for="provider in providers">
            <a :key="provider" :href="`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`">
              {{ provider }}
            </a>
          </template>
        </template>
        <a v-if="userInfo" :href="`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`"> Logout </a>
      </div>
    </nav>
    

    Pokud uživatel není přihlášený, zobrazí se přihlašovací tlačítko pro každého zprostředkovatele. Každé tlačítko odkazuje na /.auth/login/<AUTH_PROVIDER>a nastaví adresu URL přesměrování na aktuální stránku.

    Jinak pokud je uživatel již přihlášený, zobrazí se tlačítko odhlášení, na které odkazuje /.auth/logout, a také nastaví adresu URL přesměrování na aktuální stránku.

Teď byste měli vidět tuto webovou stránku v prohlížeči.

Snímek obrazovky webové aplikace Vue s tlačítky pro přihlášení

Zobrazení stavu přihlášení uživatele

Před testováním pracovního postupu ověřování zobrazíme podrobnosti o přihlášeném uživateli.

Upravte soubor angular-app/src/app/core/components/nav.component.tsa přidejte tento kód do dolní části šablony za konečnou koncovou </nav> značku.

<div class="user" *ngIf="userInfo">
  <p>Welcome</p>
  <p>{{ userInfo?.userDetails }}</p>
  <p>{{ userInfo?.identityProvider }}</p>
</div>

Poznámka:

Vlastnost userDetails může být buď uživatelské jméno, nebo e-mailová adresa v závislosti na zadané identitě použité k přihlášení.

Dokončený soubor by teď měl vypadat takto:

import { Component, OnInit } from '@angular/core';
import { UserInfo } from '../model/user-info';

@Component({
  selector: 'app-nav',
  template: `
    <nav class="menu">
      <p class="menu-label">Menu</p>
      <ul class="menu-list">
        <a routerLink="/products" routerLinkActive="router-link-active">
          <span>Products</span>
        </a>
        <a routerLink="/about" routerLinkActive="router-link-active">
          <span>About</span>
        </a>
      </ul>
    </nav>
    <nav class="menu auth">
      <p class="menu-label">Auth</p>
      <div class="menu-list auth">
        <ng-container *ngIf="!userInfo; else logout">
          <ng-container *ngFor="let provider of providers">
            <a href="/.auth/login/{{ provider }}?post_login_redirect_uri={{ redirect }}">{{ provider }}</a>
          </ng-container>
        </ng-container>
        <ng-template #logout>
          <a href="/.auth/logout?post_logout_redirect_uri={{ redirect }}">Logout</a>
        </ng-template>
      </div>
    </nav>
    <div class="user" *ngIf="userInfo">
      <p>Welcome</p>
      <p>{{ userInfo?.userDetails }}</p>
      <p>{{ userInfo?.identityProvider }}</p>
    </div>
  `,
})
export class NavComponent implements OnInit {
  providers = ['x', 'github', 'aad'];
  redirect = window.location.pathname;
  userInfo: UserInfo;

  async ngOnInit() {
    this.userInfo = await this.getUserInfo();
  }

  async getUserInfo() {
    try {
      const response = await fetch('/.auth/me');
      const payload = await response.json();
      const { clientPrincipal } = payload;
      return clientPrincipal;
    } catch (error) {
      console.error('No profile could be found');
      return undefined;
    }
  }
}

Upravte soubor react-app/src/components/NavBar.jsa přidejte tento kód do dolní části šablony JSX za konečnou koncovou </nav> značku, aby se zobrazil stav přihlášení.

{
  userInfo && (
    <div>
      <div className="user">
        <p>Welcome</p>
        <p>{userInfo && userInfo.userDetails}</p>
        <p>{userInfo && userInfo.identityProvider}</p>
      </div>
    </div>
  )
}

Poznámka:

Vlastnost userDetails může být buď uživatelské jméno, nebo e-mailová adresa v závislosti na zadané identitě použité k přihlášení.

Dokončený soubor by teď měl vypadat takto:

import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';

const NavBar = (props) => {
  const providers = ['x', 'github', 'aad'];
  const redirect = window.location.pathname;
  const [userInfo, setUserInfo] = useState();

  useEffect(() => {
    (async () => {
      setUserInfo(await getUserInfo());
    })();
  }, []);

  async function getUserInfo() {
    try {
      const response = await fetch('/.auth/me');
      const payload = await response.json();
      const { clientPrincipal } = payload;
      return clientPrincipal;
    } catch (error) {
      console.error('No profile could be found');
      return undefined;
    }
  }

  return (
    <div className="column is-2">
      <nav className="menu">
        <p className="menu-label">Menu</p>
        <ul className="menu-list">
          <NavLink to="/products" activeClassName="active-link">
            Products
          </NavLink>
          <NavLink to="/about" activeClassName="active-link">
            About
          </NavLink>
        </ul>
        {props.children}
      </nav>
      <nav className="menu auth">
        <p className="menu-label">Auth</p>
        <div className="menu-list auth">
          {!userInfo &&
            providers.map((provider) => (
              <a key={provider} href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}>
                {provider}
              </a>
            ))}
          {userInfo && <a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}>Logout</a>}
        </div>
      </nav>
      {userInfo && (
        <div>
          <div className="user">
            <p>Welcome</p>
            <p>{userInfo && userInfo.userDetails}</p>
            <p>{userInfo && userInfo.identityProvider}</p>
          </div>
        </div>
      )}
    </div>
  );
};

export default NavBar;

Upravte soubor svelte-app/src/components/NavBar.sveltea přidejte tento kód do dolní části šablony za konečnou koncovou </nav> značku, aby se zobrazil stav přihlášení.

{#if userInfo}
<div class="user">
  <p>Welcome</p>
  <p>{userInfo && userInfo.userDetails}</p>
  <p>{userInfo && userInfo.identityProvider}</p>
</div>
{/if}

Poznámka:

Vlastnost userDetails může být buď uživatelské jméno, nebo e-mailová adresa v závislosti na zadané identitě použité k přihlášení.

Dokončený soubor by teď měl vypadat takto:

<script>
  import { onMount } from 'svelte';
  import { Link } from 'svelte-routing';

  const providers = ['x', 'github', 'aad'];
  const redirect = window.location.pathname;
  let userInfo = undefined;

  onMount(async () => (userInfo = await getUserInfo()));

  async function getUserInfo() {
    try {
      const response = await fetch('/.auth/me');
      const payload = await response.json();
      const { clientPrincipal } = payload;
      return clientPrincipal;
    } catch (error) {
      console.error('No profile could be found');
      return undefined;
    }
  }

  function getProps({ href, isPartiallyCurrent, isCurrent }) {
    const isActive = href === '/' ? isCurrent : isPartiallyCurrent || isCurrent;

    // The object returned here is spread on the anchor element's attributes
    if (isActive) {
      return { class: 'router-link-active' };
    }
    return {};
  }
</script>

<div class="column is-2">
  <nav class="menu">
    <p class="menu-label">Menu</p>
    <ul class="menu-list">
      <Link to="/products" {getProps}>Products</Link>
      <Link to="/about" {getProps}>About</Link>
    </ul>
  </nav>
  <nav class="menu auth">
    <p class="menu-label">Auth</p>
    <div class="menu-list auth">
      {#if !userInfo}
        {#each providers as provider (provider)}
          <a href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}>
            {provider}
          </a>
        {/each}
      {/if}
      {#if userInfo}
        <a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}>
          Logout
        </a>
      {/if}
    </div>
  </nav>
  {#if userInfo}
    <div class="user">
      <p>Welcome</p>
      <p>{userInfo && userInfo.userDetails}</p>
      <p>{userInfo && userInfo.identityProvider}</p>
    </div>
  {/if}
</div>

Upravte soubor vue-app/src/components/nav-bar.vuea přidejte tento kód do dolní části šablony za konečnou koncovou </nav> značku, aby se zobrazil stav přihlášení:

<div class="user" v-if="userInfo">
  <p>Welcome</p>
  <p>{{ userInfo.userDetails }}</p>
  <p>{{ userInfo.identityProvider }}</p>
</div>

Poznámka:

Vlastnost userDetails může být buď uživatelské jméno, nebo e-mailová adresa v závislosti na zadané identitě použité k přihlášení.

Dokončený soubor by teď měl vypadat takto:

<script>
  export default {
    name: 'NavBar',
    data() {
      return {
        userInfo: {
          type: Object,
          default() {},
        },
        providers: ['x', 'github', 'aad'],
        redirect: window.location.pathname,
      };
    },
    methods: {
      async getUserInfo() {
        try {
          const response = await fetch('/.auth/me');
          const payload = await response.json();
          const { clientPrincipal } = payload;
          return clientPrincipal;
        } catch (error) {
          console.error('No profile could be found');
          return undefined;
        }
      },
    },
    async created() {
      this.userInfo = await this.getUserInfo();
    },
  };
</script>

<template>
  <div column is-2>
    <nav class="menu">
      <p class="menu-label">Menu</p>
      <ul class="menu-list">
        <router-link to="/products">Products</router-link>
        <router-link to="/about">About</router-link>
      </ul>
    </nav>
    <nav class="menu auth">
      <p class="menu-label">Auth</p>
      <div class="menu-list auth">
        <template v-if="!userInfo">
          <template v-for="provider in providers">
            <a :key="provider" :href="`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`">{{ provider }}</a>
          </template>
        </template>
        <a v-if="userInfo" :href="`/.auth/logout?post_logout_redirect_uri=${redirect}`">Logout</a>
      </div>
    </nav>
    <div class="user" v-if="userInfo">
      <p>Welcome</p>
      <p>{{ userInfo.userDetails }}</p>
      <p>{{ userInfo.identityProvider }}</p>
    </div>
  </div>
</template>

Místní testování ověřování

Všechno je teď na místě. Posledním krokem je otestovat, jestli všechno funguje podle očekávání.

  1. Ve webové aplikaci vyberte jednoho z poskytovatelů identity, které se mají přihlásit.

  2. Budete přesměrováni na tuto stránku:

    Snímek obrazovky znázorňující falešnou ověřovací obrazovku SWA CLI

    Jedná se o falešnou obrazovku ověřování, kterou poskytuje rozhraní příkazového řádku SWA, které umožňuje místní testování ověřování poskytnutím podrobností o uživateli.

  3. Zadejte mslearn jako uživatelské jméno a 1234 pro ID uživatele.

  4. Vyberte volbu Přihlásit se.

    Po přihlášení budete přesměrováni na předchozí stránku. Přihlašovací tlačítka se nahradila tlačítkem odhlášení. Pod tlačítkem odhlásit se můžete také zobrazit svoje uživatelské jméno a vybraný poskytovatel.

    Teď, když jste zkontrolovali, že všechno funguje podle očekávání místně, je čas nasadit změny.

  5. Spuštěnou aplikaci a rozhraní API můžete zastavit stisknutím kláves Ctrl-C v obou terminálech.

Nasazení změn

  1. V editoru Visual Studio Code otevřete paletu příkazů stisknutím klávesy F1.

  2. Zadejte a vyberte Git: Potvrdit vše.

  3. Jako zprávu potvrzení zadejte Add authentication a stiskněte Enter.

  4. Stisknutím klávesy F1 otevřete paletu příkazů.

  5. Zadejte a vyberte Git: Nasdílení změn a stiskněte Enter.

Po nasdílení změn počkejte na spuštění procesu sestavení a nasazení. Změny by se měly zobrazit v nasazené aplikaci.

Další kroky

Vaše aplikace teď podporuje ověřování uživatelů a vaším dalším krokem je omezit některé části aplikace na neověřené uživatele.