Ćwiczenie — dodawanie uwierzytelniania użytkowników
Aplikacja internetowa listy zakupów wymaga uwierzytelniania użytkownika. W tym ćwiczeniu zaimplementujesz logowanie i wylogowywanie w aplikacji oraz wyświetlisz bieżący stan logowania użytkownika.
W tym ćwiczeniu należy wykonać następujące kroki:
- Zainstaluj interfejs wiersza polecenia usługi Static Web Apps na potrzeby programowania lokalnego.
- Uruchom aplikację i interfejs API lokalnie z lokalną emulacją uwierzytelniania.
- Dodaj przyciski logowania dla wielu dostawców uwierzytelniania.
- Dodaj przycisk wyloguj się, jeśli użytkownik jest zalogowany.
- Wyświetl stan logowania użytkownika.
- Przetestuj przepływ pracy uwierzytelniania lokalnie.
- Wdróż zaktualizowaną aplikację.
Przygotowanie do programowania lokalnego
Interfejs wiersza polecenia usługi Static Web Apps, znany również jako interfejs wiersza polecenia swa, to lokalne narzędzie programistyczne, które umożliwia lokalne uruchamianie aplikacji internetowej i interfejsu API oraz emulowanie serwerów uwierzytelniania i autoryzacji.
Otwórz terminal na swoim komputerze.
Zainstaluj interfejs wiersza polecenia swa, uruchamiając następujące polecenie.
npm install -g @azure/static-web-apps-cli
Lokalne uruchamianie aplikacji
Teraz uruchom aplikację i interfejs API lokalnie z serwerem deweloperów. Dzięki temu będzie można zobaczyć i przetestować zmiany w miarę ich wprowadzania w kodzie.
Otwórz projekt w programie Visual Studio Code.
W programie Visual Studio Code otwórz paletę poleceń, naciskając F1.
Wprowadź i wybierz pozycję Terminal: Utwórz nowy zintegrowany terminal.
Przejdź do folderu preferowanej struktury frontonu w następujący sposób:
cd angular-app
cd react-app
cd svelte-app
cd vue-app
Uruchom aplikację kliencką frontonu przy użyciu serwera programistycznego.
npm start
npm start
npm run dev
npm run serve
Pozostaw ten serwer uruchomiony w tle. Teraz uruchom emulator interfejsu API i serwera uwierzytelniania przy użyciu interfejsu wiersza polecenia programu SWA.
W programie Visual Studio Code otwórz paletę poleceń, naciskając F1.
Wprowadź i wybierz pozycję Terminal: Utwórz nowy zintegrowany terminal.
Uruchom interfejs wiersza polecenia swa, uruchamiając następujące polecenie:
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
Przejdź do
http://localhost:4280
.
Ostatni port używany przez interfejs wiersza polecenia programu SWA różni się od tego, który był wcześniej widoczny, ponieważ używa zwrotnego serwera proxy do przekazywania żądań do trzech różnych składników:
- Serwer deweloperów platformy
- Emulator uwierzytelniania i autoryzacji
- Interfejs API hostowany przez środowisko uruchomieniowe usługi Functions
Niech aplikacja pozostanie uruchomiona podczas modyfikowania kodu.
Uzyskiwanie stanu logowania użytkownika
Najpierw musisz uzyskać dostęp do stanu logowania użytkownika, wykonując zapytanie w /.auth/me
kliencie.
Utwórz plik
angular-app/src/app/core/models/user-info.ts
i dodaj następujący kod, aby reprezentować interfejs informacji o użytkowniku.export interface UserInfo { identityProvider: string; userId: string; userDetails: string; userRoles: string[]; }
Zmodyfikuj plik
angular-app/src/app/core/components/nav.component.ts
i dodaj następującą metodę wNavComponent
klasie .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; } }
Utwórz nową właściwość
userInfo
klasy i zapisz wynik funkcjigetUserInfo()
asynchronicznych podczas inicjowania składnika. ZaimplementujOnInit
interfejs i zaktualizuj instrukcje importowania w celu zaimportowaniaOnInit
iUserInfo
. Ten kod pobiera informacje o użytkowniku po zainicjowaniu składnika.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(); } // ... }
Zmodyfikuj plik
react-app/src/components/NavBar.js
i dodaj następujący kod w górnej części funkcji. Ten kod pobiera informacje o użytkowniku, gdy składnik ładuje i zapisuje je w stanie.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 ( // ...
Edytuj plik
svelte-app/src/components/NavBar.svelte
i dodaj następujący kod w sekcji skryptu. Ten kod pobiera informacje o użytkowniku podczas ładowania składnika.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; } }
Zmodyfikuj plik
vue-app/src/components/nav-bar.vue
i dodajuserInfo
go do obiektu danych.data() { return { userInfo: { type: Object, default() {}, }, }; },
Dodaj metodę
getUserInfo()
do sekcji metody .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; } }, },
Dodaj punkt zaczepienia
created
cyklu życia do składnika.async created() { this.userInfo = await this.getUserInfo(); },
Po utworzeniu składnika informacje o użytkowniku są pobierane automatycznie.
Dodawanie przycisków logowania i wylogowywanie
Informacje o użytkowniku będą widoczne undefined
, jeśli nie są zalogowani, więc zmiany nie będą widoczne na razie. Nadszedł czas, aby dodać przyciski logowania dla różnych dostawców.
Edytuj plik
angular-app/src/app/core/components/nav.component.ts
, aby dodać listę dostawców wNavComponent
klasie .providers = ['x', 'github', 'aad'];
Dodaj następującą
redirect
właściwość, aby przechwycić bieżący adres URL przekierowania po zalogowaniu.redirect = window.location.pathname;
Dodaj następujący kod do szablonu po pierwszym
</nav>
elemecie, aby wyświetlić przyciski logowania i wylogowywanie.<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>
Jeśli użytkownik nie jest zalogowany, zostanie wyświetlony przycisk logowania dla każdego dostawcy. Każdy przycisk łączy się z
/.auth/login/<AUTH_PROVIDER>
elementem i ustawia adres URL przekierowania na bieżącą stronę.W przeciwnym razie, jeśli użytkownik jest już zalogowany, zostanie wyświetlony przycisk wylogowywania, który powoduje wyświetlenie linków do
/.auth/logout
elementu , a także ustawienie adresu URL przekierowania na bieżącą stronę.
Powinna zostać wyświetlona ta strona internetowa w przeglądarce.
Edytuj plik
react-app/src/components/NavBar.js
, aby dodać listę dostawców w górnej części funkcji.const providers = ['x', 'github', 'aad'];
Dodaj następującą
redirect
zmienną poniżej pierwszej zmiennej, aby przechwycić bieżący adres URL przekierowania po zalogowaniu.const redirect = window.location.pathname;
Dodaj następujący kod do szablonu JSX po pierwszym
</nav>
elemercie, aby wyświetlić przyciski logowania i wylogowywanie.<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>
Jeśli użytkownik nie jest zalogowany, zostanie wyświetlony przycisk logowania dla każdego dostawcy. Każdy przycisk łączy się z
/.auth/login/<AUTH_PROVIDER>
elementem i ustawia adres URL przekierowania na bieżącą stronę.W przeciwnym razie, jeśli użytkownik jest już zalogowany, zostanie wyświetlony przycisk wylogowywania, który łączy się
/.auth/logout
z , a także ustawia adres URL przekierowania do bieżącej strony.
Powinna zostać wyświetlona ta strona internetowa w przeglądarce.
Edytuj plik
svelte-app/src/components/NavBar.svelte
, aby dodać listę dostawców w górnej części skryptu.const providers = ['x', 'github', 'aad'];
Dodaj następującą
redirect
zmienną poniżej pierwszej zmiennej, aby przechwycić bieżący adres URL przekierowania po zalogowaniu.const redirect = window.location.pathname;
Dodaj następujący kod do szablonu po pierwszym
</nav>
elemecie, aby wyświetlić przyciski logowania i wylogowywanie.<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>
Jeśli użytkownik nie jest zalogowany, zostanie wyświetlony przycisk logowania dla każdego dostawcy. Każdy przycisk łączy się z
/.auth/login/<AUTH_PROVIDER>
elementem i ustawia adres URL przekierowania na bieżącą stronę.W przeciwnym razie, jeśli użytkownik jest już zalogowany, zostanie wyświetlony przycisk wylogowywania, który łączy się
/.auth/logout
z , a także ustawia adres URL przekierowania do bieżącej strony.
Powinna zostać wyświetlona ta strona internetowa w przeglądarce.
Edytuj plik
vue-app/src/components/nav-bar.vue
i dodaj listę dostawców do obiektu danych.providers: ['x', 'github', 'aad'],
Dodaj następującą
redirect
właściwość, aby przechwycić bieżący adres URL przekierowania po zalogowaniu.redirect: window.location.pathname,
Dodaj następujący kod do szablonu po pierwszym
</nav>
elemecie, aby wyświetlić przyciski logowania i wylogowywanie.<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>
Jeśli użytkownik nie jest zalogowany, zostanie wyświetlony przycisk logowania dla każdego dostawcy. Każdy przycisk łączy się z
/.auth/login/<AUTH_PROVIDER>
elementem i ustawia adres URL przekierowania na bieżącą stronę.W przeciwnym razie, jeśli użytkownik jest już zalogowany, zostanie wyświetlony przycisk wylogowywania, który łączy się
/.auth/logout
z , a także ustawia adres URL przekierowania do bieżącej strony.
Powinna zostać wyświetlona ta strona internetowa w przeglądarce.
Wyświetlanie stanu logowania użytkownika
Przed przetestowaniem przepływu pracy uwierzytelniania wyświetlmy szczegółowe informacje o zalogowanym użytkowniku.
Zmodyfikuj plik angular-app/src/app/core/components/nav.component.ts
i dodaj ten kod do dołu szablonu po ostatnim tagu zamykającym </nav>
.
<div class="user" *ngIf="userInfo">
<p>Welcome</p>
<p>{{ userInfo?.userDetails }}</p>
<p>{{ userInfo?.identityProvider }}</p>
</div>
Uwaga
Właściwość userDetails
może być nazwą użytkownika lub adresem e-mail, w zależności od tożsamości podanej do zalogowania.
Ukończony plik powinien teraz wyglądać następująco:
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;
}
}
}
Zmodyfikuj plik react-app/src/components/NavBar.js
i dodaj ten kod do dołu szablonu JSX po ostatnim tagu zamykającym </nav>
, aby wyświetlić stan logowania.
{
userInfo && (
<div>
<div className="user">
<p>Welcome</p>
<p>{userInfo && userInfo.userDetails}</p>
<p>{userInfo && userInfo.identityProvider}</p>
</div>
</div>
)
}
Uwaga
Właściwość userDetails
może być nazwą użytkownika lub adresem e-mail, w zależności od tożsamości podanej do zalogowania.
Ukończony plik powinien teraz wyglądać następująco:
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;
Zmodyfikuj plik svelte-app/src/components/NavBar.svelte
i dodaj ten kod do dołu szablonu po ostatnim tagu zamykającym </nav>
, aby wyświetlić stan logowania.
{#if userInfo}
<div class="user">
<p>Welcome</p>
<p>{userInfo && userInfo.userDetails}</p>
<p>{userInfo && userInfo.identityProvider}</p>
</div>
{/if}
Uwaga
Właściwość userDetails
może być nazwą użytkownika lub adresem e-mail, w zależności od tożsamości podanej do zalogowania.
Ukończony plik powinien teraz wyglądać następująco:
<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>
Zmodyfikuj plik vue-app/src/components/nav-bar.vue
i dodaj ten kod do dołu szablonu po ostatnim tagu zamykającym </nav>
, aby wyświetlić stan logowania:
<div class="user" v-if="userInfo">
<p>Welcome</p>
<p>{{ userInfo.userDetails }}</p>
<p>{{ userInfo.identityProvider }}</p>
</div>
Uwaga
Właściwość userDetails
może być nazwą użytkownika lub adresem e-mail, w zależności od tożsamości podanej do zalogowania.
Ukończony plik powinien teraz wyglądać następująco:
<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>
Lokalne testowanie uwierzytelniania
Wszystko jest teraz na miejscu. Ostatnim krokiem jest przetestowanie, czy wszystko działa zgodnie z oczekiwaniami.
W aplikacji internetowej wybierz jednego z dostawców tożsamości, aby się zalogować.
Nastąpi przekierowanie do tej strony:
Jest to fałszywy ekran uwierzytelniania udostępniany przez interfejs wiersza polecenia swa, który umożliwia lokalne testowanie uwierzytelniania przez podanie szczegółowych informacji o użytkowniku.
Wprowadź
mslearn
jako nazwę użytkownika i1234
identyfikator użytkownika.Wybierz Zaloguj.
Po zalogowaniu nastąpi przekierowanie do poprzedniej strony. Możesz zobaczyć, że przyciski logowania zostały zastąpione przyciskiem wylogowywaniem. Możesz również wyświetlić swoją nazwę użytkownika i wybranego dostawcę poniżej przycisku wylogowywanie.
Po sprawdzeniu, czy wszystko działa zgodnie z oczekiwaniami lokalnie, nadszedł czas na wdrożenie zmian.
Możesz zatrzymać uruchomioną aplikację i interfejs API, naciskając Ctrl-C w obu terminalach.
Wdrażanie zmian
W programie Visual Studio Code otwórz paletę poleceń, naciskając F1.
Wprowadź i wybierz pozycję Git: Zatwierdź wszystko.
Wprowadź
Add authentication
jako komunikat zatwierdzenia, a następnie naciśnij Enter.Naciśnij klawisz F1, aby otworzyć paletę poleceń.
Wprowadź i wybierz pozycję Git: Wypchnij, a następnie naciśnij Enter.
Po wypchnięciu zmian poczekaj na uruchomienie procesu kompilacji i wdrożenia. Zmiany powinny być widoczne w wdrożonej aplikacji.
Następne kroki
Aplikacja obsługuje teraz uwierzytelnianie użytkowników, a następnym krokiem jest ograniczenie niektórych części aplikacji do nieuwierzytelnionych użytkowników.