Cvičení – přidání ověřování uživatelů
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:
- Nainstalujte rozhraní příkazového řádku Static Web Apps pro místní vývoj.
- Spusťte aplikaci a rozhraní API místně s emulací místního ověřování.
- Přidání přihlašovacích tlačítek pro více zprostředkovatelů ověřování
- Pokud je uživatel přihlášený, přidejte tlačítko odhlášení.
- Zobrazí stav přihlášení uživatele.
- Místně otestujte pracovní postup ověřování.
- 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ů.
Na počítači si otevřete terminál.
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.
Otevřete projekt v editoru Visual Studio Code.
V editoru Visual Studio Code otevřete paletu příkazů stisknutím klávesy F1.
Zadejte a vyberte Terminál: Vytvořit nový integrovaný terminál.
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
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.
V editoru Visual Studio Code otevřete paletu příkazů stisknutím klávesy F1.
Zadejte a vyberte Terminál: Vytvořit nový integrovaný terminál.
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
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
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
.
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[]; }
Upravte soubor
angular-app/src/app/core/components/nav.component.ts
a přidejte do třídy následující metoduNavComponent
.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; } }
Vytvořte novou vlastnost třídy
userInfo
a uložte výsledek asynchronní funkcegetUserInfo()
při inicializaci komponenty.OnInit
Implementujte rozhraní a aktualizujte příkazy importu pro importOnInit
aUserInfo
. 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(); } // ... }
Upravte soubor
react-app/src/components/NavBar.js
a 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 ( // ...
Upravte soubor
svelte-app/src/components/NavBar.svelte
a 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; } }
Upravte soubor
vue-app/src/components/nav-bar.vue
a přidejteuserInfo
ho do datového objektu.data() { return { userInfo: { type: Object, default() {}, }, }; },
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; } }, },
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.
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'];
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;
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/logout
na 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.
Upravte soubor
react-app/src/components/NavBar.js
a přidejte seznam zprostředkovatelů v horní části funkce.const providers = ['x', 'github', 'aad'];
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;
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.
Upravte soubor
svelte-app/src/components/NavBar.svelte
a přidejte seznam zprostředkovatelů v horní části skriptu.const providers = ['x', 'github', 'aad'];
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;
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.
Upravte soubor
vue-app/src/components/nav-bar.vue
a přidejte do datového objektu seznam zprostředkovatelů.providers: ['x', 'github', 'aad'],
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,
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.
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.ts
a 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.js
a 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.svelte
a 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.vue
a 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í.
Ve webové aplikaci vyberte jednoho z poskytovatelů identity, které se mají přihlásit.
Budete přesměrováni na tuto stránku:
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.
Zadejte
mslearn
jako uživatelské jméno a1234
pro ID uživatele.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.
Spuštěnou aplikaci a rozhraní API můžete zastavit stisknutím kláves Ctrl-C v obou terminálech.
Nasazení změn
V editoru Visual Studio Code otevřete paletu příkazů stisknutím klávesy F1.
Zadejte a vyberte Git: Potvrdit vše.
Jako zprávu potvrzení zadejte
Add authentication
a stiskněte Enter.Stisknutím klávesy F1 otevřete paletu příkazů.
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.