Übung: Hinzufügen der Benutzerauthentifizierung
Ihre Web-App für Einkaufslisten benötigt eine Benutzerauthentifizierung. In dieser Übung implementieren Sie die An- und Abmeldung in Ihrer Anwendung und zeigen den aktuellen Anmeldestatus des Benutzers an.
In dieser Übung führen Sie die folgenden Schritte aus:
- Installieren der Static Web Apps CLI für die lokale Entwicklung
- Lokales Ausführen der App und API mithilfe einer Emulation der lokalen Authentifizierung
- Hinzufügen von Anmeldeschaltflächen für mehrere Authentifizierungsanbieter
- Hinzufügen einer Abmeldeschaltfläche, wenn der Benutzer angemeldet ist
- Anzeige des Anmeldestatus des Benutzers
- Lokales Testen den Authentifizierungsworkflows
- Bereitstellen der aktualisierten App
Vorbereiten für die lokale Entwicklung
Die Static Web Apps CLI, auch bekannt als SWA CLI, ist ein lokales Entwicklungstool, mit dem Sie Ihre Web-App und API lokal ausführen und Authentifizierungs- und Autorisierungsserver emulieren können.
Öffnen Sie ein Terminal auf dem Computer.
Installieren Sie die SWA-CLI mit dem folgenden Befehl.
npm install -g @azure/static-web-apps-cli
Lokales Ausführen der App
Führen Sie nun die App und API lokal auf einem Entwicklungsserver aus. Auf diese Weise können Sie Ihre Änderungen anzeigen und testen, während Sie sie im Code vornehmen.
Öffnen Sie das Projekt in Visual Studio Code.
Öffnen Sie in Visual Studio Code die Befehlspalette, indem Sie F1 drücken.
Geben Sie Terminal: Neues integriertes Terminal erstellen ein, und wählen Sie den angezeigten Eintrag aus.
Navigieren Sie wie unten dargestellt zum Ordner Ihres bevorzugten Front-End-Frameworks:
cd angular-app
cd react-app
cd svelte-app
cd vue-app
Führen Sie die Front-End-Clientanwendung auf einem Entwicklungsserver aus.
npm start
npm start
npm run dev
npm run serve
Führen Sie diesen Server im Hintergrund in weiter aus. Führen Sie nun die API und den Authentifizierungsserveremulator mithilfe der SWA-CLI aus.
Öffnen Sie in Visual Studio Code die Befehlspalette, indem Sie F1 drücken.
Geben Sie Terminal: Neues integriertes Terminal erstellen ein, und wählen Sie den angezeigten Eintrag aus.
Führen Sie die SWA-CLI mit dem folgenden Befehl aus:
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
Navigieren Sie zu
http://localhost:4280
.
Der endgültige Port, der von der SWA CLI verwendet wird, unterscheidet sich von dem, den Sie bisher gesehen haben, da er einen Reverseproxy verwendet, um Anforderungen an drei Komponenten weiterzuleiten:
- Den Entwicklungsserver Ihres Frameworks
- Den Authentifizierungs- und Autorisierungsemulator
- Die von der Functions-Runtime gehostete API
Lassen Sie die Anwendung laufen, während Sie den Code ändern.
Abrufen des Benutzeranmeldestatus
Zuerst müssen Sie auf den Anmeldestatus des Benutzers zugreifen, indem Sie im Client eine Abfrage an /.auth/me
richten.
Erstellen Sie die Datei
angular-app/src/app/core/models/user-info.ts
, und fügen Sie den folgenden Code hinzu, um die Schnittstelle für die Benutzerinformationen darzustellen.export interface UserInfo { identityProvider: string; userId: string; userDetails: string; userRoles: string[]; }
Bearbeiten Sie die Datei
angular-app/src/app/core/components/nav.component.ts
, und fügen Sie die folgende Methode zur KlasseNavComponent
hinzu.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; } }
Erstellen Sie eine neue Klasseneigenschaft
userInfo
, und speichern Sie das Ergebnis der asynchronen FunktiongetUserInfo()
, wenn die Komponente initialisiert wird. Implementieren Sie dieOnInit
-Schnittstelle, und aktualisieren Sie die Importanweisungen, umOnInit
undUserInfo
zu importieren. Dieser Code ruft die Benutzerinformationen ab, wenn die Komponente initialisiert wird.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(); } // ... }
Bearbeiten Sie die Datei
react-app/src/components/NavBar.js
, um den folgenden Code am Anfang der Funktion hinzuzufügen. Dieser Code ruft die Benutzerinformationen ab, wenn die Komponente geladen wird, und speichert sie im Zustand.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 ( // ...
Bearbeiten Sie die Datei
svelte-app/src/components/NavBar.svelte
, und fügen Sie den folgenden Code im Skriptabschnitt hinzu. Dieser Code ruft die Benutzerinformationen ab, wenn die Komponente geladen wird.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; } }
Bearbeiten Sie die Datei
vue-app/src/components/nav-bar.vue
, und fügen SieuserInfo
zum Datenobjekt hinzu.data() { return { userInfo: { type: Object, default() {}, }, }; },
Fügen Sie im Abschnitt methods die
getUserInfo()
-Methode hinzu.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; } }, },
Fügen Sie der Komponente den Lebenszyklushook
created
hinzu.async created() { this.userInfo = await this.getUserInfo(); },
Beim Anlegen der Komponente werden die Benutzerinformationen automatisch abgerufen.
Hinzufügen von An- und Abmeldeschaltflächen
Die Benutzerinformationen lauten undefined
, wenn der Benutzer nicht angemeldet ist. Ihre Änderungen sind vorerst nicht sichtbar. Es ist Zeit, Anmeldeschaltflächen für die verschiedenen Anbieter hinzuzufügen.
Bearbeiten Sie die Datei
angular-app/src/app/core/components/nav.component.ts
so, dass der KlasseNavComponent
eine Liste mit Anbietern hinzugefügt wird.providers = ['x', 'github', 'aad'];
Fügen Sie die folgende
redirect
-Eigenschaft hinzu, um die aktuelle URL für die Umleitung nach der Anmeldung zu erfassen.redirect = window.location.pathname;
Fügen Sie den folgenden Code in der Vorlage hinter dem ersten
</nav>
-Element hinzu, um die An- und Abmeldeschaltflächen anzuzeigen.<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>
Wenn der Benutzer nicht angemeldet ist, wird die Anmeldeschaltfläche für jeden Anbieter angezeigt. Jede Schaltfläche ist ein Link zu
/.auth/login/<AUTH_PROVIDER>
und legt die Umleitungs-URL auf die aktuelle Seite fest.Wenn der Benutzer hingegen bereits angemeldet ist, wird eine Abmeldeschaltfläche mit einem Link zu
/.auth/logout
angezeigt. Außerdem wird die Umleitungs-URL auf die aktuelle Seite festgelegt.
Diese Website sollte nun in Ihrem Browser angezeigt werden.
Ändern Sie die Datei
react-app/src/components/NavBar.js
so, dass am Anfang der Funktion eine Liste der Anbieter hinzugefügt wird.const providers = ['x', 'github', 'aad'];
Fügen Sie die folgende Variable
redirect
unter der ersten Variablen hinzu, um die aktuelle URL für die Umleitung nach der Anmeldung zu erfassen.const redirect = window.location.pathname;
Fügen Sie den folgenden Code in der JSX-Vorlage hinter dem ersten
</nav>
-Element hinzu, um die An- und Abmeldeschaltflächen anzuzeigen.<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>
Wenn der Benutzer nicht angemeldet ist, wird die Anmeldeschaltfläche für jeden Anbieter angezeigt. Jede Schaltfläche ist ein Link zu
/.auth/login/<AUTH_PROVIDER>
und legt die Umleitungs-URL auf die aktuelle Seite fest.Wenn der Benutzer hingegen bereits angemeldet ist, wird eine Abmeldeschaltfläche mit einem Link zu
/.auth/logout
angezeigt. Außerdem wird die Umleitungs-URL auf die aktuelle Seite festgelegt.
Diese Website sollte nun in Ihrem Browser angezeigt werden.
Ändern Sie die Datei
svelte-app/src/components/NavBar.svelte
so, dass am Anfang des Skripts eine Liste der Anbieter hinzugefügt wird.const providers = ['x', 'github', 'aad'];
Fügen Sie die folgende Variable
redirect
unter der ersten Variablen hinzu, um die aktuelle URL für die Umleitung nach der Anmeldung zu erfassen.const redirect = window.location.pathname;
Fügen Sie den folgenden Code in der Vorlage hinter dem ersten
</nav>
-Element hinzu, um die An- und Abmeldeschaltflächen anzuzeigen.<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>
Wenn der Benutzer nicht angemeldet ist, wird die Anmeldeschaltfläche für jeden Anbieter angezeigt. Jede Schaltfläche ist ein Link zu
/.auth/login/<AUTH_PROVIDER>
und legt die Umleitungs-URL auf die aktuelle Seite fest.Wenn der Benutzer hingegen bereits angemeldet ist, wird eine Abmeldeschaltfläche mit einem Link zu
/.auth/logout
angezeigt. Außerdem wird die Umleitungs-URL auf die aktuelle Seite festgelegt.
Diese Website sollte nun in Ihrem Browser angezeigt werden.
Ändern Sie die Datei
vue-app/src/components/nav-bar.vue
so, dass dem Datenobjekt eine Liste der Anbieter hinzugefügt wird.providers: ['x', 'github', 'aad'],
Fügen Sie die folgende
redirect
-Eigenschaft hinzu, um die aktuelle URL für die Umleitung nach der Anmeldung zu erfassen.redirect: window.location.pathname,
Fügen Sie den folgenden Code in der Vorlage hinter dem ersten
</nav>
-Element hinzu, um die An- und Abmeldeschaltflächen anzuzeigen.<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>
Wenn der Benutzer nicht angemeldet ist, wird die Anmeldeschaltfläche für jeden Anbieter angezeigt. Jede Schaltfläche ist ein Link zu
/.auth/login/<AUTH_PROVIDER>
und legt die Umleitungs-URL auf die aktuelle Seite fest.Wenn der Benutzer hingegen bereits angemeldet ist, wird eine Abmeldeschaltfläche mit einem Link zu
/.auth/logout
angezeigt. Außerdem wird die Umleitungs-URL auf die aktuelle Seite festgelegt.
Diese Website sollte nun in Ihrem Browser angezeigt werden.
Anzeigen des Benutzeranmeldestatus
Zeigen Sie die Details des angemeldeten Benutzers an, bevor Sie den Authentifizierungsworkflow testen.
Bearbeiten Sie die Datei angular-app/src/app/core/components/nav.component.ts
, und fügen Sie diesen Code am Ende der Vorlage hinter dem letzten schließenden </nav>
-Tag ein:
<div class="user" *ngIf="userInfo">
<p>Welcome</p>
<p>{{ userInfo?.userDetails }}</p>
<p>{{ userInfo?.identityProvider }}</p>
</div>
Hinweis
Die userDetails
-Eigenschaft kann entweder ein Benutzername oder eine E-Mail-Adresse sein, je nachdem, welche Identität zur Anmeldung verwendet wurde.
Die fertige Datei sollte jetzt wie das folgende Beispiel aussehen:
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;
}
}
}
Bearbeiten Sie die Datei react-app/src/components/NavBar.js
, und fügen Sie diesen Code am Ende der JSX-Vorlage hinter dem letzten schließenden </nav>
-Tag ein, um den Anmeldestatus anzuzeigen:
{
userInfo && (
<div>
<div className="user">
<p>Welcome</p>
<p>{userInfo && userInfo.userDetails}</p>
<p>{userInfo && userInfo.identityProvider}</p>
</div>
</div>
)
}
Hinweis
Die userDetails
-Eigenschaft kann entweder ein Benutzername oder eine E-Mail-Adresse sein, je nachdem, welche Identität zur Anmeldung verwendet wurde.
Die fertige Datei sollte jetzt wie das folgende Beispiel aussehen:
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;
Bearbeiten Sie die Datei svelte-app/src/components/NavBar.svelte
, und fügen Sie diesen Code am Ende der Vorlage hinter dem letzten schließenden </nav>
-Tag ein, um den Anmeldestatus anzuzeigen:
{#if userInfo}
<div class="user">
<p>Welcome</p>
<p>{userInfo && userInfo.userDetails}</p>
<p>{userInfo && userInfo.identityProvider}</p>
</div>
{/if}
Hinweis
Die userDetails
-Eigenschaft kann entweder ein Benutzername oder eine E-Mail-Adresse sein, je nachdem, welche Identität zur Anmeldung verwendet wurde.
Die fertige Datei sollte jetzt wie das folgende Beispiel aussehen:
<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>
Bearbeiten Sie die Datei vue-app/src/components/nav-bar.vue
, und fügen Sie diesen Code am Ende der Vorlage hinter dem letzten schließenden </nav>
-Tag ein, um den Anmeldestatus anzuzeigen:
<div class="user" v-if="userInfo">
<p>Welcome</p>
<p>{{ userInfo.userDetails }}</p>
<p>{{ userInfo.identityProvider }}</p>
</div>
Hinweis
Die userDetails
-Eigenschaft kann entweder ein Benutzername oder eine E-Mail-Adresse sein, je nachdem, welche Identität zur Anmeldung verwendet wurde.
Die fertige Datei sollte jetzt wie das folgende Beispiel aussehen:
<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>
Lokales Testen der Authentifizierung
Jetzt ist alles fertig. Im letzten Schritt müssen Sie testen, ob alles wie erwartet funktioniert.
Wählen Sie in Ihrer Web-App einen der Identitätsanbieter aus, um sich anzumelden.
Sie werden zu dieser Seite umgeleitet:
Dies ist ein nachgemachter Authentifizierungsbildschirm, der von der SWA CLI bereitgestellt wird, sodass Sie die Authentifizierung lokal testen können, indem Sie sich selbst Benutzerdetails bereitstellen.
Geben Sie
mslearn
als Benutzernamen und als1234
Benutzer-ID ein.Wählen Sie Anmelden.
Nach der Anmeldung werden Sie zur vorherigen Seite umgeleitet. Sie können erkennen, dass die Anmeldeschaltflächen durch eine Abmeldeschaltfläche ersetzt wurden. Unter der Abmeldeschaltfläche werden auch Ihr Benutzername und der ausgewählte Anbieter angezeigt.
Nachdem Sie überprüft haben, dass lokal alles wie erwartet funktioniert, können Sie Ihre Änderungen bereitstellen.
Sie können die ausgeführte App und API beenden, indem Sie in beiden Terminals STRG+C drücken.
Bereitstellen Ihrer Änderungen
Öffnen Sie in Visual Studio Code die Befehlspalette, indem Sie F1 drücken.
Geben Sie Git: Commit für alle ausführen ein, und wählen Sie den angezeigten Eintrag aus.
Geben Sie
Add authentication
als Commitnachricht ein, und drücken Sie die EINGABETASTE.Öffnen Sie die Befehlspalette, indem Sie F1 drücken.
Geben Sie Git: Push ein, wählen Sie den angezeigten Eintrag aus, und drücken Sie die EINGABETASTE.
Nachdem Sie Ihre Änderungen per Push übertragen haben, warten Sie auf die Ausführung des Build- und Bereitstellungsprozesses. Die Änderungen sollten danach in Ihrer bereitgestellten App sichtbar sein.
Nächste Schritte
Ihre Anwendung unterstützt jetzt die Benutzerauthentifizierung. Der nächste Schritt besteht darin, einige Teile der App auf nicht authentifizierte Benutzer zu beschränken.