Kurz: Přihlášení uživatelů a volání rozhraní Microsoft Graph API z jednostránkové aplikace Angular (SPA) pomocí toku ověřovacího kódu

V tomto kurzu vytvoříte jednostránkovou aplikaci Angular (SPA), která přihlásí uživatele a zavolá rozhraní Microsoft Graph API pomocí toku autorizačního kódu s PKCE. Služba SPA, kterou vytvoříte, používá knihovnu MICROSOFT Authentication Library (MSAL) pro Angular v2.

V tomto kurzu:

  • Registrace aplikace v Centru pro správu Microsoft Entra
  • Vytvoření projektu Angular pomocí npm
  • Přidání kódu pro podporu přihlášení a odhlášení uživatele
  • Přidání kódu pro volání rozhraní Microsoft Graph API
  • Otestování aplikace

MsAL Angular v2 vylepšuje msAL Angular v1 tím, že podporuje tok autorizačního kódu v prohlížeči místo implicitního toku udělení. MSAL Angular v2 nepodporuje implicitní tok.

Požadavky

  • Node.js pro spuštění místního webového serveru.
  • Visual Studio Code nebo jiný editor pro úpravy souborů projektu

Jak funguje ukázková aplikace

Diagram showing the authorization code flow in a single-page application

Ukázková aplikace vytvořená v tomto kurzu umožňuje angular SPA dotazovat rozhraní Microsoft Graph API nebo webové rozhraní API, které přijímá tokeny vydané platformou Microsoft Identity Platform. Používá knihovnu MSAL (Microsoft Authentication Library) pro Angular v2, obálku knihovny MSAL.js v2. MSAL Angular umožňuje aplikacím Angular 9+ ověřovat podnikové uživatele pomocí Microsoft Entra ID a také uživatelů s účty Microsoft a sociálními identitami, jako jsou Facebook, Google a LinkedIn. Knihovna také umožňuje aplikacím získat přístup ke cloudovým službám Microsoftu a Microsoft Graphu.

V tomto scénáři se po přihlášení uživatele vyžádá přístupový token a přidá se do požadavků HTTP prostřednictvím autorizační hlavičky. Nástroj MSAL zpracovává získání a obnovení tokenů.

Knihovny

V tomto kurzu se používají následující knihovny:

Knihovna Popis
MSAL Angular Microsoft Authentication Library for JavaScript Angular Wrapper
Prohlížeč MSAL Balíček prohlížeče Microsoft Authentication Library pro JavaScript v2

Zdrojový kód pro všechny knihovny MSAL.js najdete v microsoft-authentication-library-for-js úložišti na GitHubu.

Získání dokončené ukázky kódu

Dáváte přednost stažení dokončeného ukázkového projektu pro tento kurz? Klonování ms-identity-javascript-angular-spa

git clone https://github.com/Azure-Samples/ms-identity-javascript-angular-spa.git

Pokud chcete pokračovat v kurzu a sestavit aplikaci sami, přejděte k další části, zaregistrujte identifikátory aplikace a záznamu.

Registrace identifikátorů aplikace a záznamů

Tip

Postup v tomto článku se může mírně lišit v závislosti na portálu, od který začínáte.

K dokončení registrace zadejte název aplikace, zadejte podporované typy účtů a přidejte identifikátor URI přesměrování. Po registraci se v podokně Přehled aplikace zobrazí identifikátory potřebné ve zdrojovém kódu aplikace.

  1. Přihlaste se do Centra pro správu Microsoft Entra jako alespoň vývojář aplikací.
  2. Pokud máte přístup k více tenantům, pomocí ikony Nastavení v horní nabídce přepněte na tenanta, ve kterém chcete aplikaci zaregistrovat z nabídky Adresáře a předplatná.
  3. Přejděte k aplikacím> identit>Registrace aplikací.
  4. Vyberte Nová registrace.
  5. Zadejte název aplikace, například Angular-SPA-auth-code.
  6. U podporovaných typů účtů vyberte Pouze účty v tomto organizačním adresáři. Pokud chcete získat informace o různých typech účtů, vyberte možnost Nápověda pro výběr .
  7. V části Identifikátor URI přesměrování (volitelné) pomocí rozevírací nabídky vyberte Jednostránkové aplikace (SPA) a zadejte http://localhost:4200 do textového pole.
  8. Vyberte Zaregistrovat.
  9. Po dokončení registrace se zobrazí podokno Přehled aplikace. Poznamenejte si ID adresáře (tenanta) a ID aplikace (klienta), které se má použít ve zdrojovém kódu aplikace.

Vytvoření projektu

  1. Otevřete Visual Studio Code a vyberte >Soubor otevřít složku.... Přejděte a vyberte umístění, ve kterém chcete projekt vytvořit.

  2. Výběrem možnosti Terminál>nový terminál otevřete nový terminál.

    1. Možná budete muset přepnout typy terminálů. Vyberte šipku + dolů vedle ikony v terminálu a vyberte příkazový řádek.
  3. Spuštěním následujících příkazů vytvořte nový projekt Angular s názvem msal-angular-tutorial, nainstalujte knihovny komponent Angular Material, MSAL Browser, MSAL Angular a vygenerujte součásti domů a profilu.

    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
    

Konfigurace aplikace a úprava základního uživatelského rozhraní

  1. Otevřete soubor src,app/app.module.ts. Je MsalModule potřeba je přidat imports spolu s konstantouisIE.MsalInterceptor Přidáte také moduly materiálu. Celý obsah souboru nahraďte následujícím fragmentem kódu:

    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 {}
    
  2. Nahraďte následující hodnoty hodnotami získanými z Centra pro správu Microsoft Entra. Další informace o dostupných konfigurovatelných možnostech naleznete v tématu Inicializace klientských aplikací.

    • clientId – Identifikátor aplikace, označovaný také jako klient. Nahraďte Enter_the_Application_Id_Here hodnotou ID aplikace (klienta), která byla zaznamenána dříve ze stránky přehledu registrované aplikace.
    • authority - Skládá se ze dvou částí:
      • Instance je koncový bod poskytovatele cloudu. V případě hlavního nebo globálního cloudu Azure zadejte https://login.microsoftonline.com. Zkontrolujte různé dostupné koncové body v národních cloudech.
      • ID tenanta je identifikátor tenanta, ve kterém je aplikace zaregistrovaná. _Enter_the_Tenant_Info_HereHodnotu ID adresáře (tenanta) zaznamenanou dříve na stránce přehledu registrované aplikace nahraďte hodnotou ID adresáře (tenanta).
    • redirectUri – umístění, kde autorizační server odešle uživatele po úspěšném autorizaci aplikace a udělení autorizačního kódu nebo přístupového tokenu. Nahraďte Enter_the_Redirect_Uri_Herehttp://localhost:4200.
  3. Otevřete soubor src,app/app-routing.module.ts a přidejte trasy do komponent domů a profilů . Celý obsah souboru nahraďte následujícím fragmentem kódu:

    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 {}
    
  4. Otevřete src/app/app.component.html a nahraďte stávající kód následujícím kódem:

    <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>
    
  5. Otevřete soubor src/style.css a definujte šablony stylů 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%;
    }
    
  6. Otevřete soubor src,app/app.component.css a přidejte do aplikace styly CSS:

    .toolbar-spacer {
      flex: 1 1 auto;
    }
    
    a.title {
      color: white;
    }
    

Přihlášení pomocí automaticky otevíraných oken

  1. Otevřete soubor src/app/app.component.ts a nahraďte obsah souboru následujícím způsobem, abyste se přihlásili uživatele pomocí automaticky otevíraného okna:

    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;
      }
    }
    

Přihlášení pomocí přesměrování

  1. Aktualizujte soubor src,app/app.module.ts pro spuštění MsalRedirectComponentsouboru . Jedná se o vyhrazenou komponentu přesměrování, která zpracovává přesměrování. MsalModule Změňte import a AppComponent bootstrap tak, aby vypadal takto:

    ...
    import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import
    ...
      bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here
    ...
    
  2. Otevřete src/index.html a nahraďte celý obsah souboru následujícím fragmentem kódu, který přidá <app-redirect> selektor:

    <!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>
    
  3. Otevřete soubor src/app/app.component.ts a nahraďte kód následujícím kódem, abyste se přihlásili uživatele pomocí přesměrování na plný rámec:

    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;
      }
    }
    
  4. Přejděte na src/app/home/home.component.ts a nahraďte celý obsah souboru následujícím fragmentem kódu a přihlaste se k odběru LOGIN_SUCCESS události:

    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);
          });
      }
    }
    

Podmíněné vykreslování

Aby bylo možné vykreslit určité uživatelské rozhraní pouze pro ověřené uživatele, musí se komponenty přihlásit k odběru MsalBroadcastService , aby se zjistilo, jestli byli přihlášeni uživatelé a interakce byla dokončena.

  1. MsalBroadcastService Přidejte do src,app/app.component.ts a přihlaste se k odběru inProgress$ pozorovatelného účtu, abyste zkontrolovali, jestli je interakce dokončená, a před vykreslením uživatelského rozhraní je přihlášený účet. Váš kód by teď měl vypadat takto:

    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();
      }
    }
    
  2. Aktualizujte kód v souboru src,app/home/home.component.ts a zkontrolujte také, jestli se má před aktualizací uživatelského rozhraní dokončit interakce. Váš kód by teď měl vypadat takto:

    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;
      }
    }
    
  3. Nahraďte kód v src/app/home/home.component.html následujícím podmíněným zobrazením:

    <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>
    

Implementace angular Guard

Třída MsalGuard je ta, kterou můžete použít k ochraně tras a před přístupem k chráněné trase vyžadovat ověření. Následující postup přidá trasu MsalGuardProfile . Profile Ochrana trasy znamená, že i když se uživatel nepřihlásí pomocí Login tlačítka, pokud se pokusí o přístup k Profile trase nebo ho Profile vybere, MsalGuard vyzve uživatele, aby se před zobrazením Profile stránky ověřil přes automaticky otevírané okno nebo přesměrování.

MsalGuard je třída pohodlí, kterou můžete použít ke zlepšení uživatelského prostředí, ale nemělo by se spoléhat na zabezpečení. Útočníci můžou potenciálně obejít ochranu na straně klienta a měli byste zajistit, aby server nevrátil žádná data, ke kterým by neměl uživatel přistupovat.

  1. MsalGuard Přidejte třídu jako zprostředkovatele v aplikaci v src/app/app.module.ts a přidejte konfigurace pro MsalGuard. Rozsahy potřebné pro získání tokenů lze později poskytnout v nástroji authRequesta typ interakce pro Guard lze nastavit na Redirect nebo Popup. Váš kód by měl vypadat takto:

    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 {}
    
  2. MsalGuard Nastavte trasy, které chcete chránit v src, aplikaci nebo 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 {}
    
  3. Upravte volání přihlášení v src/app/app.component.ts tak, aby se zohlednila authRequest sada v konfiguracích ochrany. Váš kód by teď měl vypadat takto:

    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();
      }
    }
    

Získání tokenu

Průsečík Angular

MSAL Angular poskytuje Interceptor třídu, která automaticky získává tokeny pro odchozí požadavky, které používají klienta Angular http ke známým chráněným prostředkům.

  1. Interceptor Přidejte třídu jako zprostředkovatele do aplikace v src/app/app.module.ts s jeho konfiguracemi. Váš kód by teď měl vypadat takto:

    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 {}
    

    Chráněné prostředky jsou poskytovány jako protectedResourceMap. Adresy URL, které zadáte v kolekci protectedResourceMap , rozlišují malá a velká písmena. Pro každý prostředek přidejte obory, o které se žádá, aby se vrátily v přístupovém tokenu.

    Příklad:

    • ["user.read"] pro Microsoft Graph
    • ["<Application ID URL>/scope"] pro vlastní webová rozhraní API (to znamená api://<Application ID>/access_as_user)

    Upravte hodnoty v následujícím protectedResourceMap popisu:

    • Enter_the_Graph_Endpoint_Here je instance rozhraní Microsoft Graph API, se kterým by aplikace měla komunikovat. V případě globálního koncového bodu rozhraní Microsoft Graph API nahraďte tento řetězec řetězcem https://graph.microsoft.com. Koncové body v národních cloudových nasazeních najdete v dokumentaci k Microsoft Graphu v národních cloudových nasazeních .
  2. Nahraďte kód v src/app/profile/profile.component.ts k načtení profilu uživatele požadavkem HTTP a nahraďte GRAPH_ENDPOINT ho koncovým bodem Microsoft Graphu:

    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;
          });
      }
    }
    
  3. Nahraďte uživatelské rozhraní v src/app/profile/profile.component.html , aby se zobrazily informace o profilu:

    <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>
    

Odhlásit

  1. Aktualizujte kód v src/app/app.component.html tak, aby podmíněně zobrazoval Logout tlačítko:

    <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>
    

Odhlášení pomocí přesměrování

  1. Aktualizujte kód v src/app/app.component.ts , aby se uživatel odhlasil pomocí přesměrování:

    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();
      }
    }
    

Odhlášení pomocí automaticky otevíraných oken

  1. Aktualizujte kód v src/app/app.component.ts a odhlaste uživatele pomocí automaticky otevíraných oken:

    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();
      }
    }
    

Testování kódu

  1. Spusťte webový server tak, aby naslouchal portu spuštěním následujících příkazů na příkazovém řádku ze složky aplikace:

    npm install
    npm start
    
  2. V prohlížeči zadejte http://localhost:4200a měla by se zobrazit stránka, která vypadá takto.

    Web browser displaying sign-in dialog

  3. Výběrem možnosti Přijmout udělte aplikaci oprávnění k vašemu profilu. K tomu dojde poprvé, když se začnete přihlašovat.

    Content dialog displayed in web browser

  4. Po vyjádření souhlasu se zobrazí následující: Pokud souhlasíte s požadovanými oprávněními, webová aplikace zobrazí úspěšnou přihlašovací stránku.

    Results of a successful sign-in in the web browser

  5. Výběrem možnosti Profil zobrazíte informace o profilu uživatele vrácené v odpovědi z volání rozhraní Microsoft Graph API:

    Profile information from Microsoft Graph displayed in the browser

Přidání oborů a delegovaných oprávnění

Rozhraní Microsoft Graph API vyžaduje obor User.Read ke čtení profilu uživatele. Obor User.Read se automaticky přidá do každé registrace aplikace. Jiná rozhraní API pro Microsoft Graph a vlastní rozhraní API pro váš back-endový server můžou vyžadovat další obory. Rozhraní Microsoft Graph API například vyžaduje obor Mail.Read , aby bylo možné zobrazit seznam e-mailů uživatele.

Když přidáváte obory, můžou se uživatelům zobrazit výzva k poskytnutí dodatečného souhlasu s přidanými obory.

Poznámka:

Když zvýšíte počet oborů, může se uživateli zobrazit výzva k zadání dalších souhlasů.

Nápověda a podpora

Pokud potřebujete pomoc, chcete nahlásit problém nebo se chcete dozvědět o možnostech podpory, přečtěte si nápovědu a podporu pro vývojáře.

Další kroky

  • Přečtěte si další informace o vytvoření jednostránkové aplikace React (SPA), která přihlašuje uživatele v následující vícedílné sérii kurzů.