Esercitazione: Accedere agli utenti e chiamare l'API Microsoft Graph da un'applicazione a pagina singola Angular usando il flusso del codice di autenticazione
In questa esercitazione si creerà un'applicazione a pagina singola Angular che consente agli utenti di accedere e chiamare l'API Microsoft Graph usando il flusso del codice di autorizzazione con PKCE. L'applicazione a pagina singola creata usa Microsoft Authentication Library (MSAL) per Angular v2.
Contenuto dell'esercitazione:
- Registrare l'applicazione nell'interfaccia di amministrazione di Microsoft Entra
- Creare un progetto Angular con
npm
- Aggiungere il codice per supportare l'accesso e la disconnessione
- Aggiungere il codice per chiamare l'API Microsoft Graph
- Testare l'app
MSAL Angular v2 migliora MSAL Angular v1 supportando il flusso del codice di autorizzazione nel browser invece del flusso di concessione implicita. MSAL Angular v2 non supporta il flusso implicito.
Prerequisiti
- Node.js per l'esecuzione di un server Web locale.
- Visual Studio Code o un altro editor per la modifica dei file di progetto.
Funzionamento dell'app di esempio
L'applicazione di esempio creata in questa esercitazione consente a un'applicazione a pagina singola Angular di eseguire query sull'API Microsoft Graph o su un'API Web che accetta token rilasciati da Microsoft Identity Platform. Usa Microsoft Authentication Library (MSAL) per Angular v2, un wrapper della libreria MSAL.js v2. MSAL Angular consente alle applicazioni Angular 9+ di autenticare gli utenti aziendali usando l'ID Microsoft Entra e anche gli utenti con account Microsoft e identità di social networking come Facebook, Google e LinkedIn. La libreria consente inoltre di ottenere l'accesso a servizi cloud Microsoft e a Microsoft Graph.
Per questo scenario, dopo l'accesso di un utente, viene richiesto un token di accesso che viene aggiunto a richieste HTTP tramite l'intestazione dell'autorizzazione. L'acquisizione e il rinnovo dei token vengono gestiti da Microsoft Authentication Library (MSAL).
Librerie
Questa esercitazione usa le librerie seguenti:
Libreria | Descrizione |
---|---|
MSAL Angular | Microsoft Authentication Library per il wrapper Angular JavaScript |
MSAL Browser | Pacchetto del browser Microsoft Authentication Library per JavaScript v2 |
È possibile trovare il codice sorgente per tutte le librerie MSAL.js nel microsoft-authentication-library-for-js
repository su GitHub.
Ottenere l'esempio di codice completo
Si preferisce scaricare invece il progetto di esempio completato per questa esercitazione? Clonare ms-identity-javascript-angular-spa
git clone https://github.com/Azure-Samples/ms-identity-javascript-angular-spa.git
Per continuare con l'esercitazione e compilare manualmente l'applicazione, passare alla sezione successiva, Registrare l'applicazione e gli identificatori di record.
Registrare l'applicazione e gli identificatori di record
Suggerimento
I passaggi descritti in questo articolo possono variare leggermente in base al portale da cui si inizia.
Per completare la registrazione, specificare un nome per l'applicazione, specificare i tipi di account supportati e aggiungere un URI di reindirizzamento. Dopo la registrazione, nel riquadro Panoramica dell'applicazione vengono visualizzati gli identificatori necessari nel codice sorgente dell'applicazione.
- Accedere all'interfaccia di amministrazione di Microsoft Entra come almeno uno sviluppatore di applicazioni.
- Se si ha accesso a più tenant, usare l'icona Impostazioni nel menu in alto per passare al tenant in cui si vuole registrare l'applicazione dal menu Directory e sottoscrizioni.
- Passare a Applicazioni> di identità>Registrazioni app.
- Seleziona Nuova registrazione.
- Immettere un nome per l'applicazione, ad esempio Angular-SPA-auth-code.
- Per Tipi di account supportati selezionare Account solo in questa directory dell'organizzazione. Per informazioni sui diversi tipi di account, selezionare l'opzione ?
- In URI di reindirizzamento (facoltativo) usare il menu a discesa per selezionare Applicazione a pagina singola (SPA) e immettere
http://localhost:4200
nella casella di testo. - Selezionare Registra.
- Al termine della registrazione, viene visualizzato il riquadro Panoramica dell'applicazione. Registrare l'ID directory (tenant) e l'ID applicazione (client) da usare nel codice sorgente dell'applicazione.
Creare il progetto
Aprire Visual Studio Code, selezionare File>Apri cartella.... Passare a e selezionare il percorso in cui creare il progetto.
Aprire un nuovo terminale selezionando Terminale>Nuovo terminale.
- Potrebbe essere necessario cambiare i tipi di terminale. Selezionare la freccia giù accanto all'icona + nel terminale e selezionare Prompt dei comandi.
Eseguire i comandi seguenti per creare un nuovo progetto Angular con il nome
msal-angular-tutorial
, installare librerie dei componenti di Angular Material, MSAL Browser, MSAL Angular e generare componenti home e profilo.npm install -g @angular/cli ng new msal-angular-tutorial --routing=true --style=css --strict=false cd msal-angular-tutorial npm install @angular/material @angular/cdk npm install @azure/msal-browser @azure/msal-angular ng generate component home ng generate component profile
Configurare l'applicazione e modificare l'interfaccia utente di base
Aprire src/app/app.module.ts.
MsalInterceptor
EMsalModule
devono essere aggiunti insieme allaimports
isIE
costante . Si aggiungeranno anche i moduli materiali. Sostituire l'intero contenuto del file con il frammento di codice seguente:import { BrowserModule } from "@angular/platform-browser"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { NgModule } from "@angular/core"; import { MatButtonModule } from "@angular/material/button"; import { MatToolbarModule } from "@angular/material/toolbar"; import { MatListModule } from "@angular/material/list"; import { AppRoutingModule } from "./app-routing.module"; import { AppComponent } from "./app.component"; import { HomeComponent } from "./home/home.component"; import { ProfileComponent } from "./profile/profile.component"; import { MsalModule, MsalRedirectComponent } from "@azure/msal-angular"; import { PublicClientApplication } from "@azure/msal-browser"; const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1; @NgModule({ declarations: [AppComponent, HomeComponent, ProfileComponent], imports: [ BrowserModule, BrowserAnimationsModule, AppRoutingModule, MatButtonModule, MatToolbarModule, MatListModule, MsalModule.forRoot( new PublicClientApplication({ auth: { clientId: "Enter_the_Application_Id_here", // Application (client) ID from the app registration authority: "Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here", // The Azure cloud instance and the app's sign-in audience (tenant ID, common, organizations, or consumers) redirectUri: "Enter_the_Redirect_Uri_Here", // This is your redirect URI }, cache: { cacheLocation: "localStorage", storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11 }, }), null, null ), ], providers: [], bootstrap: [AppComponent, MsalRedirectComponent], }) export class AppModule {}
Sostituire i valori seguenti con i valori ottenuti dall'interfaccia di amministrazione di Microsoft Entra. Per altre informazioni sulle opzioni configurabili disponibili, vedere Inizializzare le applicazioni client.
clientId
- Identificatore dell'applicazione, detto anche client. SostituireEnter_the_Application_Id_Here
con il valore ID applicazione (client) registrato in precedenza dalla pagina di panoramica dell'applicazione registrata.authority
- Questo è composto da due parti:- L'istanza è l'endpoint del provider di servizi cloud. Per il cloud globale o principale di Azure, immettere
https://login.microsoftonline.com
. Verificare i diversi endpoint disponibili nei cloud nazionali. - L'ID tenant è l'identificatore del tenant in cui è registrata l'applicazione. Sostituire con
_Enter_the_Tenant_Info_Here
il valore ID directory (tenant) registrato in precedenza dalla pagina di panoramica dell'applicazione registrata.
- L'istanza è l'endpoint del provider di servizi cloud. Per il cloud globale o principale di Azure, immettere
redirectUri
: il percorso in cui il server di autorizzazione invia l'utente dopo che l'app è stata autorizzata correttamente e ha concesso un codice di autorizzazione o un token di accesso. SostituisciEnter_the_Redirect_Uri_Here
conhttp://localhost:4200
.
Aprire src/app/app-routing.module.ts e aggiungere route ai componenti home e profilo . Sostituire l'intero contenuto del file con il frammento di codice seguente:
import { NgModule } from "@angular/core"; import { Routes, RouterModule } from "@angular/router"; import { BrowserUtils } from "@azure/msal-browser"; import { HomeComponent } from "./home/home.component"; import { ProfileComponent } from "./profile/profile.component"; const routes: Routes = [ { path: "profile", component: ProfileComponent, }, { path: "", component: HomeComponent, }, ]; const isIframe = window !== window.parent && !window.opener; @NgModule({ imports: [ RouterModule.forRoot(routes, { // Don't perform initial navigation in iframes or popups initialNavigation: !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup() ? "enabledNonBlocking" : "disabled", // Set to enabledBlocking to use Angular Universal }), ], exports: [RouterModule], }) export class AppRoutingModule {}
Aprire src/app/app.component.html e sostituire il codice esistente con il codice seguente:
<mat-toolbar color="primary"> <a class="title" href="/">{{ title }}</a> <div class="toolbar-spacer"></div> <a mat-button [routerLink]="['profile']">Profile</a> <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button> </mat-toolbar> <div class="container"> <!--This is to avoid reload during acquireTokenSilent() because of hidden iframe --> <router-outlet *ngIf="!isIframe"></router-outlet> </div>
Aprire src/style.css per definire il codice CSS:
@import "~@angular/material/prebuilt-themes/deeppurple-amber.css"; html, body { height: 100%; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } .container { margin: 1%; }
Aprire src/app/app.component.css per aggiungere stili CSS all'applicazione:
.toolbar-spacer { flex: 1 1 auto; } a.title { color: white; }
Accedere con popup
Aprire src/app/app.component.ts e sostituire il contenuto del file con il comando seguente per accedere a un utente usando una finestra popup:
import { MsalService } from '@azure/msal-angular'; import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { title = 'msal-angular-tutorial'; isIframe = false; loginDisplay = false; constructor(private authService: MsalService) { } ngOnInit() { this.isIframe = window !== window.parent && !window.opener; } login() { this.authService.loginPopup() .subscribe({ next: (result) => { console.log(result); this.setLoginDisplay(); }, error: (error) => console.log(error) }); } setLoginDisplay() { this.loginDisplay = this.authService.instance.getAllAccounts().length > 0; } }
Accedere con i reindirizzamenti
Aggiornare src/app/app.module.ts per eseguire il bootstrap di
MsalRedirectComponent
. Si tratta di un componente di reindirizzamento dedicato, che gestisce i reindirizzamenti. Modificare l'importazione eAppComponent
ilMsalModule
bootstrap in modo che sia simile al seguente:... import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import ... bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here ...
Aprire src/index.html e sostituire l'intero contenuto del file con il frammento di codice seguente, che aggiunge il
<app-redirect>
selettore:<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>msal-angular-tutorial</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <app-root></app-root> <app-redirect></app-redirect> </body> </html>
Aprire src/app/app.component.ts e sostituire il codice con il codice seguente per accedere a un utente usando un reindirizzamento full-frame:
import { MsalService } from '@azure/msal-angular'; import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { title = 'msal-angular-tutorial'; isIframe = false; loginDisplay = false; constructor(private authService: MsalService) { } ngOnInit() { this.isIframe = window !== window.parent && !window.opener; } login() { this.authService.loginRedirect(); } setLoginDisplay() { this.loginDisplay = this.authService.instance.getAllAccounts().length > 0; } }
Passare a src/app/home/home.component.ts e sostituire l'intero contenuto del file con il frammento di codice seguente per sottoscrivere l'evento
LOGIN_SUCCESS
:import { Component, OnInit } from '@angular/core'; import { MsalBroadcastService, MsalService } from '@azure/msal-angular'; import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser'; import { filter } from 'rxjs/operators'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { } ngOnInit(): void { this.msalBroadcastService.msalSubject$ .pipe( filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS), ) .subscribe((result: EventMessage) => { console.log(result); }); } }
Rendering condizionale
Per eseguire il rendering di determinati utenti dell'interfaccia utente solo per gli utenti autenticati, i componenti devono sottoscrivere MsalBroadcastService
per verificare se gli utenti hanno eseguito l'accesso e l'interazione è stata completata.
MsalBroadcastService
Aggiungere a src/app/app.component.ts e sottoscrivere l'osservabileinProgress$
per verificare se l'interazione è completa e un account è connesso prima del rendering dell'interfaccia utente. Il codice ora dovrebbe essere simile al seguente:import { Component, OnInit, OnDestroy } from '@angular/core'; import { MsalService, MsalBroadcastService } from '@azure/msal-angular'; import { InteractionStatus } from '@azure/msal-browser'; import { Subject } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit, OnDestroy { title = 'msal-angular-tutorial'; isIframe = false; loginDisplay = false; private readonly _destroying$ = new Subject<void>(); constructor(private broadcastService: MsalBroadcastService, private authService: MsalService) { } ngOnInit() { this.isIframe = window !== window.parent && !window.opener; this.broadcastService.inProgress$ .pipe( filter((status: InteractionStatus) => status === InteractionStatus.None), takeUntil(this._destroying$) ) .subscribe(() => { this.setLoginDisplay(); }) } login() { this.authService.loginRedirect(); } setLoginDisplay() { this.loginDisplay = this.authService.instance.getAllAccounts().length > 0; } ngOnDestroy(): void { this._destroying$.next(undefined); this._destroying$.complete(); } }
Aggiornare il codice in src/app/home/home.component.ts per verificare anche se l'interazione deve essere completata prima dell'aggiornamento dell'interfaccia utente. Il codice ora dovrebbe essere simile al seguente:
import { Component, OnInit } from '@angular/core'; import { MsalBroadcastService, MsalService } from '@azure/msal-angular'; import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser'; import { filter } from 'rxjs/operators'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { loginDisplay = false; constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { } ngOnInit(): void { this.msalBroadcastService.msalSubject$ .pipe( filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS), ) .subscribe((result: EventMessage) => { console.log(result); }); this.msalBroadcastService.inProgress$ .pipe( filter((status: InteractionStatus) => status === InteractionStatus.None) ) .subscribe(() => { this.setLoginDisplay(); }) } setLoginDisplay() { this.loginDisplay = this.authService.instance.getAllAccounts().length > 0; } }
Sostituire il codice in src/app/home/home.component.html con le visualizzazioni condizionali seguenti:
<div *ngIf="!loginDisplay"> <p>Please sign-in to see your profile information.</p> </div> <div *ngIf="loginDisplay"> <p>Login successful!</p> <p>Request your profile information by clicking Profile above.</p> </div>
Implementare Angular Guard
La MsalGuard
classe è una che è possibile usare per proteggere le route e richiedere l'autenticazione prima di accedere alla route protetta. I passaggi seguenti aggiungono alla MsalGuard
Profile
route. La protezione della Profile
route significa che, anche se un utente non esegue l'accesso usando il Login
pulsante , se tenta di accedere Profile
alla route o seleziona il Profile
pulsante, MsalGuard
chiede all'utente di eseguire l'autenticazione tramite popup o reindirizzamento prima di visualizzare la Profile
pagina.
MsalGuard
è una classe di praticità che è possibile usare per migliorare l'esperienza utente, ma non deve essere basata sulla sicurezza. Gli utenti malintenzionati possono potenzialmente aggirare le guardie lato client e assicurarsi che il server non restituisca dati a cui l'utente non deve accedere.
Aggiungere la
MsalGuard
classe come provider nell'applicazione in src/app/app.module.ts e aggiungere le configurazioni per .MsalGuard
Gli ambiti necessari per acquisire i token in un secondo momento possono essere forniti inauthRequest
e il tipo di interazione per Guard può essere impostato suRedirect
oPopup
. Il codice dovrebbe essere simile al seguente:import { BrowserModule } from "@angular/platform-browser"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { NgModule } from "@angular/core"; import { MatButtonModule } from "@angular/material/button"; import { MatToolbarModule } from "@angular/material/toolbar"; import { MatListModule } from "@angular/material/list"; import { AppRoutingModule } from "./app-routing.module"; import { AppComponent } from "./app.component"; import { HomeComponent } from "./home/home.component"; import { ProfileComponent } from "./profile/profile.component"; import { MsalModule, MsalRedirectComponent, MsalGuard, } from "@azure/msal-angular"; // MsalGuard added to imports import { PublicClientApplication, InteractionType, } from "@azure/msal-browser"; // InteractionType added to imports const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1; @NgModule({ declarations: [AppComponent, HomeComponent, ProfileComponent], imports: [ BrowserModule, BrowserAnimationsModule, AppRoutingModule, MatButtonModule, MatToolbarModule, MatListModule, MsalModule.forRoot( new PublicClientApplication({ auth: { clientId: "Enter_the_Application_Id_here", authority: "Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here", redirectUri: "Enter_the_Redirect_Uri_Here", }, cache: { cacheLocation: "localStorage", storeAuthStateInCookie: isIE, }, }), { interactionType: InteractionType.Redirect, // MSAL Guard Configuration authRequest: { scopes: ["user.read"], }, }, null ), ], providers: [ MsalGuard, // MsalGuard added as provider here ], bootstrap: [AppComponent, MsalRedirectComponent], }) export class AppModule {}
Impostare nelle
MsalGuard
route da proteggere in src/app/app-routing.module.ts:import { NgModule } from "@angular/core"; import { Routes, RouterModule } from "@angular/router"; import { BrowserUtils } from "@azure/msal-browser"; import { HomeComponent } from "./home/home.component"; import { ProfileComponent } from "./profile/profile.component"; import { MsalGuard } from "@azure/msal-angular"; const routes: Routes = [ { path: "profile", component: ProfileComponent, canActivate: [MsalGuard], }, { path: "", component: HomeComponent, }, ]; const isIframe = window !== window.parent && !window.opener; @NgModule({ imports: [ RouterModule.forRoot(routes, { // Don't perform initial navigation in iframes or popups initialNavigation: !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup() ? "enabledNonBlocking" : "disabled", // Set to enabledBlocking to use Angular Universal }), ], exports: [RouterModule], }) export class AppRoutingModule {}
Modificare le chiamate di accesso in src/app/app.component.ts in modo da tenere conto del
authRequest
set nelle configurazioni di protezione. Il codice dovrebbe ora essere simile al seguente:import { Component, OnInit, OnDestroy, Inject } from '@angular/core'; import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular'; import { InteractionStatus, RedirectRequest } from '@azure/msal-browser'; import { Subject } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit, OnDestroy { title = 'msal-angular-tutorial'; isIframe = false; loginDisplay = false; private readonly _destroying$ = new Subject<void>(); constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { } ngOnInit() { this.isIframe = window !== window.parent && !window.opener; this.broadcastService.inProgress$ .pipe( filter((status: InteractionStatus) => status === InteractionStatus.None), takeUntil(this._destroying$) ) .subscribe(() => { this.setLoginDisplay(); }) } login() { if (this.msalGuardConfig.authRequest){ this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest); } else { this.authService.loginRedirect(); } } setLoginDisplay() { this.loginDisplay = this.authService.instance.getAllAccounts().length > 0; } ngOnDestroy(): void { this._destroying$.next(undefined); this._destroying$.complete(); } }
Acquisire un token
Intercettore Angular
Microsoft Authentication Library per Angular fornisce una classe Interceptor
che acquisisce automaticamente i token per le richieste in uscita che usano il client http
Angular per le risorse protette note.
Aggiungere la
Interceptor
classe come provider all'applicazione in src/app/app.module.ts, con le relative configurazioni. Il codice dovrebbe ora essere simile al seguente:import { BrowserModule } from "@angular/platform-browser"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { NgModule } from "@angular/core"; import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http"; // Import import { MatButtonModule } from "@angular/material/button"; import { MatToolbarModule } from "@angular/material/toolbar"; import { MatListModule } from "@angular/material/list"; import { AppRoutingModule } from "./app-routing.module"; import { AppComponent } from "./app.component"; import { HomeComponent } from "./home/home.component"; import { ProfileComponent } from "./profile/profile.component"; import { MsalModule, MsalRedirectComponent, MsalGuard, MsalInterceptor, } from "@azure/msal-angular"; // Import MsalInterceptor import { InteractionType, PublicClientApplication, } from "@azure/msal-browser"; const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1; @NgModule({ declarations: [AppComponent, HomeComponent, ProfileComponent], imports: [ BrowserModule, BrowserAnimationsModule, AppRoutingModule, MatButtonModule, MatToolbarModule, MatListModule, HttpClientModule, MsalModule.forRoot( new PublicClientApplication({ auth: { clientId: "Enter_the_Application_Id_Here", authority: "Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here", redirectUri: "Enter_the_Redirect_Uri_Here", }, cache: { cacheLocation: "localStorage", storeAuthStateInCookie: isIE, }, }), { interactionType: InteractionType.Redirect, authRequest: { scopes: ["user.read"], }, }, { interactionType: InteractionType.Redirect, // MSAL Interceptor Configuration protectedResourceMap: new Map([ ["Enter_the_Graph_Endpoint_Here/v1.0/me", ["user.read"]], ]), } ), ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: MsalInterceptor, multi: true, }, MsalGuard, ], bootstrap: [AppComponent, MsalRedirectComponent], }) export class AppModule {}
Le risorse protette vengono fornite come .
protectedResourceMap
Gli URL specificati nella raccoltaprotectedResourceMap
fanno distinzione tra maiuscole e minuscole. Per ogni risorsa, aggiungere gli ambiti da restituire nel token di accesso.Ad esempio:
["user.read"]
per Microsoft Graph["<Application ID URL>/scope"]
per le API Web personalizzate, ovveroapi://<Application ID>/access_as_user
Modificare i valori in
protectedResourceMap
come descritto di seguito:Enter_the_Graph_Endpoint_Here
è l'istanza dell'API Microsoft Graph con cui l'applicazione dovrà comunicare. Per l'endpoint dell'API Microsoft Graph globale , sostituire questa stringa conhttps://graph.microsoft.com
. Per gli endpoint delle distribuzioni di cloud nazionali, vedere Distribuzioni di cloud nazionali nella documentazione di Microsoft Graph.
Sostituire il codice in src/app/profile/profile.component.ts per recuperare il profilo di un utente con una richiesta HTTP e sostituire con
GRAPH_ENDPOINT
l'endpoint di Microsoft Graph:import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; const GRAPH_ENDPOINT = 'Enter_the_Graph_Endpoint_Here/v1.0/me'; type ProfileType = { givenName?: string, surname?: string, userPrincipalName?: string, id?: string }; @Component({ selector: 'app-profile', templateUrl: './profile.component.html', styleUrls: ['./profile.component.css'] }) export class ProfileComponent implements OnInit { profile!: ProfileType; constructor( private http: HttpClient ) { } ngOnInit() { this.getProfile(); } getProfile() { this.http.get(GRAPH_ENDPOINT) .subscribe(profile => { this.profile = profile; }); } }
Sostituire l'interfaccia utente in src/app/profile/profile.component.html per visualizzare le informazioni sul profilo:
<div> <p><strong>First Name: </strong> {{profile?.givenName}}</p> <p><strong>Last Name: </strong> {{profile?.surname}}</p> <p><strong>Email: </strong> {{profile?.userPrincipalName}}</p> <p><strong>Id: </strong> {{profile?.id}}</p> </div>
Disconnettersi
Aggiornare il codice in src/app/app.component.html per visualizzare in modo condizionale un
Logout
pulsante:<mat-toolbar color="primary"> <a class="title" href="/">{{ title }}</a> <div class="toolbar-spacer"></div> <a mat-button [routerLink]="['profile']">Profile</a> <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button> <button mat-raised-button *ngIf="loginDisplay" (click)="logout()">Logout</button> </mat-toolbar> <div class="container"> <!--This is to avoid reload during acquireTokenSilent() because of hidden iframe --> <router-outlet *ngIf="!isIframe"></router-outlet> </div>
Disconnettersi usando i reindirizzamenti
Aggiornare il codice in src/app/app.component.ts per disconnettere un utente usando i reindirizzamenti:
import { Component, OnInit, OnDestroy, Inject } from '@angular/core'; import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular'; import { InteractionStatus, RedirectRequest } from '@azure/msal-browser'; import { Subject } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit, OnDestroy { title = 'msal-angular-tutorial'; isIframe = false; loginDisplay = false; private readonly _destroying$ = new Subject<void>(); constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { } ngOnInit() { this.isIframe = window !== window.parent && !window.opener; this.broadcastService.inProgress$ .pipe( filter((status: InteractionStatus) => status === InteractionStatus.None), takeUntil(this._destroying$) ) .subscribe(() => { this.setLoginDisplay(); }) } login() { if (this.msalGuardConfig.authRequest){ this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest); } else { this.authService.loginRedirect(); } } logout() { // Add log out function here this.authService.logoutRedirect({ postLogoutRedirectUri: 'http://localhost:4200' }); } setLoginDisplay() { this.loginDisplay = this.authService.instance.getAllAccounts().length > 0; } ngOnDestroy(): void { this._destroying$.next(undefined); this._destroying$.complete(); } }
Disconnettersi con popup
Aggiornare il codice in src/app/app.component.ts per disconnettere un utente usando popup:
import { Component, OnInit, OnDestroy, Inject } from '@angular/core'; import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular'; import { InteractionStatus, PopupRequest } from '@azure/msal-browser'; import { Subject } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit, OnDestroy { title = 'msal-angular-tutorial'; isIframe = false; loginDisplay = false; private readonly _destroying$ = new Subject<void>(); constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { } ngOnInit() { this.isIframe = window !== window.parent && !window.opener; this.broadcastService.inProgress$ .pipe( filter((status: InteractionStatus) => status === InteractionStatus.None), takeUntil(this._destroying$) ) .subscribe(() => { this.setLoginDisplay(); }) } login() { if (this.msalGuardConfig.authRequest){ this.authService.loginPopup({...this.msalGuardConfig.authRequest} as PopupRequest) .subscribe({ next: (result) => { console.log(result); this.setLoginDisplay(); }, error: (error) => console.log(error) }); } else { this.authService.loginPopup() .subscribe({ next: (result) => { console.log(result); this.setLoginDisplay(); }, error: (error) => console.log(error) }); } } logout() { // Add log out function here this.authService.logoutPopup({ mainWindowRedirectUri: "/" }); } setLoginDisplay() { this.loginDisplay = this.authService.instance.getAllAccounts().length > 0; } ngOnDestroy(): void { this._destroying$.next(undefined); this._destroying$.complete(); } }
Eseguire test del codice
Avviare il server Web in modo che rimanga in ascolto sulla porta eseguendo questi comandi da un prompt della riga di comando nella cartella dell'applicazione:
npm install npm start
Nel browser immettere
http://localhost:4200
e dovrebbe essere visualizzata una pagina simile alla seguente.Selezionare Accetta per concedere le autorizzazioni dell'app al profilo. Questa operazione si verifica la prima volta che si inizia ad accedere.
Dopo il consenso, se si acconsente alle autorizzazioni richieste, l'applicazione Web visualizza una pagina di accesso riuscita.
Selezionare Profilo per visualizzare le informazioni sul profilo utente restituite nella risposta dalla chiamata all'API Microsoft Graph:
Aggiungere ambiti e autorizzazioni delegate
L'API Microsoft Graph richiede l'ambito User.Read per leggere il profilo di un utente. L'ambito User.Read viene aggiunto automaticamente a ogni registrazione dell'app. Altre API per Microsoft Graph e API personalizzate per il server back-end potrebbero richiedere altri ambiti. Ad esempio, l'API Microsoft Graph richiede l'ambito Mail.Read per visualizzare la posta elettronica dell'utente.
Quando si aggiungono ambiti, agli utenti potrebbe essere richiesto di fornire un consenso aggiuntivo per gli ambiti aggiunti.
Nota
Con l'aumentare del numero di ambiti è possibile che all'utente venga chiesto di esprimere anche altri tipi di consenso.
Assistenza e supporto
Se è necessaria assistenza, si vuole segnalare un problema o si vogliono ottenere informazioni sulle opzioni di supporto, vedere Assistenza e supporto per gli sviluppatori.
Passaggi successivi
- Per altre informazioni, è possibile creare un'applicazione a pagina singola React che consente agli utenti di accedere alla serie di esercitazioni in più parti seguente.