教程:从 Angular 单页应用程序将用户登录并调用 Microsoft Graph APITutorial: Sign in users and call the Microsoft Graph API from an Angular single-page application

在本教程中,你将构建一个 Angular 单页应用程序 (SPA),它使用户登录并调用 Microsoft Graph API。In this tutorial, you build an Angular single-page application (SPA) that signs in users and calls the Microsoft Graph API.

本教程的内容:In this tutorial:

  • 使用 npm 创建 Angular 项目Create an Angular project with npm
  • 在 Azure 门户中注册应用程序Register the application in the Azure portal
  • 添加代码以支持用户登录和注销Add code to support user sign-in and sign-out
  • 添加代码以调用 Microsoft Graph APIAdd code to call Microsoft Graph API
  • 测试应用程序Test the app

先决条件Prerequisites

示例应用工作原理How the sample app works

示意图,展示了本教程中生成的示例应用的工作原理

本教程中创建的示例应用程序使 Angular SPA 能够查询 Microsoft Graph API 或 Web API,该 API 接受 Microsoft 标识平台颁发的令牌。The sample application created in this tutorial enables an Angular SPA to query the Microsoft Graph API or a web API that accepts tokens issued by the Microsoft identity platform. 它使用适用于 Angular 的 Microsoft 身份验证库 (MSAL),这是核心 MSAL.js 库的包装器。It uses the Microsoft Authentication Library (MSAL) for Angular, a wrapper of the core MSAL.js library. MSAL Angular 可以让 Angular 6+ 应用程序使用 Azure Active Directory (Azure AD) 对企业用户进行身份验证。MSAL Angular enables Angular 6+ applications to authenticate enterprise users by using Azure Active Directory (Azure AD). 使用此库,应用程序还可以获取对 Azure 云服务和 Microsoft Graph 的访问权限。The library also enables applications to get access to Azure cloud services and Microsoft Graph.

在此方案中,用户登录后请求了访问令牌,并通过授权标头将其添加到 HTTP 请求。In this scenario, after a user signs in, an access token is requested and added to HTTP requests through the authorization header. 令牌获取和续订通过 MSAL 处理。Token acquisition and renewal are handled by MSAL.

Libraries

本教程使用以下库:This tutorial uses the following library:

Library 说明Description
msal.jsmsal.js 适用于 JavaScript Angular 的 Microsoft 身份验证库包装器Microsoft Authentication Library for JavaScript Angular Wrapper

可以在 GitHub 上的 AzureAD/microsoft-authentication-library-for-js 存储库中找到 MSAL.js 库的源代码。You can find the source code for the MSAL.js library in the AzureAD/microsoft-authentication-library-for-js repository on GitHub.

创建项目Create your project

使用以下 npm 命令生成一个新的 Angular 应用程序:Generate a new Angular application by using the following npm commands:

npm install -g @angular/cli@8                    # Install the Angular CLI
ng new my-application --routing=true --style=css # Generate a new Angular app
cd my-application                                # Change to the app directory
npm install @angular/material@8 @angular/cdk@8   # Install the Angular Material component library (optional, for UI)
npm install msal @azure/msal-angular             # Install MSAL and MSAL Angular in your application
ng generate component page-name                  # To add a new page (such as a home or profile page)

注册应用程序Register your application

按照说明在 Azure 门户中注册单页应用程序Follow the instructions to register a single-page application in the Azure portal.

在注册的应用“概述”页上,记下“应用程序(客户端) ID”值供稍后使用。 On the app Overview page of your registration, note the Application (client) ID value for later use.

注册 http://localhost:4200/ 作为“重定向 URI”,并启用隐式授权设置。Register your Redirect URI value as http://localhost:4200/ and enable implicit grant settings.

配置应用程序Configure the application

  1. 在 src/app 文件夹中,编辑 app.module.ts,将 MSALModule 添加到 imports 并添加 isIE 常量: In the src/app folder, edit app.module.ts and add MSALModule to imports as well as the isIE constant:

    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        AppRoutingModule,
        MsalModule.forRoot({
          auth: {
            clientId: 'Enter_the_Application_Id_here', // This is your client ID
            authority: 'Enter_the_Cloud_Instance_Id_Here'/'Enter_the_Tenant_Info_Here', // This is your tenant ID
            redirectUri: 'Enter_the_Redirect_Uri_Here'// This is your redirect URI
          },
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11
          },
        }, {
          popUp: !isIE,
          consentScopes: [
            'https://microsoftgraph.chinacloudapi.cn/user.read',
            'openid',
            'profile',
          ],
          unprotectedResources: [],
          protectedResourceMap: [
            ['https://microsoftgraph.chinacloudapi.cn/v1.0/me', ['https://microsoftgraph.chinacloudapi.cn/user.read']]
          ],
          extraQueryParameters: {}
        })
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    

    替换以下值:Replace these values:

    值名称Value name 关于About
    Enter_the_Application_Id_HereEnter_the_Application_Id_Here 在应用程序注册的“概览”页中,这是你的“应用程序(客户端) ID”值。 On the Overview page of your application registration, this is your Application (client) ID value.
    Enter_the_Cloud_Instance_Id_HereEnter_the_Cloud_Instance_Id_Here 这是 Azure 云的实例。This is the instance of the Azure cloud. 对于主要云或全球 Azure 云,请输入 https://login.partner.microsoftonline.cnFor the main or global Azure cloud, enter https://login.partner.microsoftonline.cn. 对于国家/地区云(例如中国云),请参阅国家/地区云For national clouds (for example, China), see National clouds.
    Enter_the_Tenant_Info_HereEnter_the_Tenant_Info_Here 设置为以下选项之一:如果应用程序支持此组织目录中的帐户,请将此值替换为目录(租户)ID 或租户名称(例如 contoso.microsoft.com)。Set to one of the following options: If your application supports accounts in this organizational directory, replace this value with the directory (tenant) ID or tenant name (for example, contoso.microsoft.com). 如果应用程序支持“任何组织目录中的帐户”,请将此值替换为 organizationsIf your application supports accounts in any organizational directory, replace this value with organizations.
    Enter_the_Redirect_Uri_HereEnter_the_Redirect_Uri_Here 替换为 http://localhost:4200Replace with http://localhost:4200.

    有关可用的可配置选项的详细信息,请阅读初始化客户端应用程序For more information about available configurable options, see Initialize client applications.

  2. 在同一文件的顶部,添加以下 import 语句:At the top of the same file, add the following import statement:

    import { MsalModule, MsalInterceptor } from '@azure/msal-angular';
    
  3. 将以下 import 语句添加到 src/app/app.component.ts 的顶部:Add the following import statements to the top of src/app/app.component.ts:

    import { MsalService, BroadcastService } from '@azure/msal-angular';
    import { Component, OnInit } from '@angular/core';
    

将用户登录Sign in a user

将以下代码添加到 AppComponent,以将用户登录:Add the following code to AppComponent to sign in a user:

export class AppComponent implements OnInit {
    constructor(private broadcastService: BroadcastService, private authService: MsalService) { }

    ngOnInit() { }

    login() {
        const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;

        if (isIE) {
          this.authService.loginRedirect({
            extraScopesToConsent: ["https://microsoftgraph.chinacloudapi.cn/user.read", "openid", "profile"]
          });
        } else {
          this.authService.loginPopup({
            extraScopesToConsent: ["https://microsoftgraph.chinacloudapi.cn/user.read", "openid", "profile"]
          });
        }
    }
}

提示

对于 Internet Explorer 用户,建议使用 loginRedirectWe recommend using loginRedirect for Internet Explorer users.

获取令牌Acquire a token

Angular 侦听器Angular Interceptor

MSAL Angular 向已知的受保护资源提供一个 Interceptor 类,该类可自动为使用 Angular http 客户端的传出请求获取令牌。MSAL Angular provides an Interceptor class that automatically acquires tokens for outgoing requests that use the Angular http client to known protected resources.

首先,将 Interceptor 类作为提供程序包含在应用程序中:First, include the Interceptor class as a provider to your application:

import { MsalInterceptor, MsalModule } from "@azure/msal-angular";
import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http";

@NgModule({
    // ...
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: MsalInterceptor,
            multi: true
        }
    ]
}

接下来,以 protectedResourceMap 的形式提供受保护资源到 MsalModule.forRoot() 的映射,并将这些作用域包含在 consentScopes 中。Next, provide a map of protected resources to MsalModule.forRoot() as protectedResourceMap and include those scopes in consentScopes. protectedResourceMap 集合中输入的 URL 区分大小写。The URLs you provide in the protectedResourceMap collection are case-sensitive.

@NgModule({
  // ...
  imports: [
    // ...
    MsalModule.forRoot({
      auth: {
        clientId: 'Enter_the_Application_Id_here', // This is your client ID
        authority: 'https://login.partner.microsoftonline.cn/Enter_the_Tenant_Info_Here', // This is your tenant info
        redirectUri: 'Enter_the_Redirect_Uri_Here' // This is your redirect URI
      },
      cache: {
        cacheLocation: 'localStorage',
        storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11
      },
    },
    {
      popUp: !isIE,
      consentScopes: [
        'https://microsoftgraph.chinacloudapi.cn/user.read',
        'openid',
        'profile',
      ],
      unprotectedResources: [],
      protectedResourceMap: [
        ['https://microsoftgraph.chinacloudapi.cn/v1.0/me', ['https://microsoftgraph.chinacloudapi.cn/user.read']]
      ],
      extraQueryParameters: {}
    })
  ],
});

最后,使用 HTTP 请求检索用户的配置文件:Finally, retrieve a user's profile with an HTTP request:

const graphMeEndpoint = "https://microsoftgraph.chinacloudapi.cn/v1.0/me";

getProfile() {
  this.http.get(graphMeEndpoint).toPromise()
    .then(profile => {
      this.profile = profile;
    });
}

acquireTokenSilent、acquireTokenPopup、acquireTokenRedirectacquireTokenSilent, acquireTokenPopup, acquireTokenRedirect

MSAL 使用三个方法来获取令牌:acquireTokenRedirectacquireTokenPopupacquireTokenSilentMSAL uses three methods to acquire tokens: acquireTokenRedirect, acquireTokenPopup, and acquireTokenSilent. 但是,对于 Angular 应用,我们建议改用 MsalInterceptor 类,如前一部分中所示。However, we recommend using the MsalInterceptor class instead for Angular apps, as shown in the previous section.

以无提示方式获取用户令牌Get a user token silently

acquireTokenSilent 方法处理令牌获取和续订,且无需用户交互。The acquireTokenSilent method handles token acquisitions and renewal without user interaction. 首次执行 loginRedirectloginPopup 方法后,通常使用 acquireTokenSilent 获取用于在后续调用中访问受保护资源的令牌。After the loginRedirect or loginPopup method is executed for the first time, acquireTokenSilent is commonly used to obtain tokens used to access protected resources in later calls. 进行请求或续订令牌的调用时,以静默方式进行。Calls to request or renew tokens are made silently.

const requestObj = {
    scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"]
};

this.authService.acquireTokenSilent(requestObj).then(function (tokenResponse) {
    // Callback code here
    console.log(tokenResponse.accessToken);
}).catch(function (error) {
    console.log(error);
});

在该代码中,scopes 包含所请求的需要在 API 的访问令牌中返回的作用域。In that code, scopes contains scopes being requested to be returned in the access token for the API.

例如:For example:

  • Microsoft Graph 的 ["https://microsoftgraph.chinacloudapi.cn/user.read"]["https://microsoftgraph.chinacloudapi.cn/user.read"] for Microsoft Graph
  • 自定义 Web API 的 ["<Application ID URL>/scope"](即 api://<Application ID>/access_as_user["<Application ID URL>/scope"] for custom web APIs (that is, api://<Application ID>/access_as_user)

以交互方式获取用户令牌Get a user token interactively

有时需要让用户与 Microsoft 标识平台终结点进行交互。Sometimes you need the user to interact with the Microsoft identity platform endpoint. 例如:For example:

  • 由于密码已过期,用户可能需要重新输入凭据。Users might need to reenter their credentials because their password has expired.
  • 应用程序正在请求访问用户需要许可的其他资源范围。Your application is requesting access to additional resource scopes that the user needs to consent to.
  • 需要双重身份验证。Two-factor authentication is required.

对于大多数应用程序,建议的模式是先调用 acquireTokenSilent,然后捕获异常,然后再调用 acquireTokenPopup(或 acquireTokenRedirect),以启动交互式请求。The recommended pattern for most applications is to call acquireTokenSilent first, then catch the exception, and then call acquireTokenPopup (or acquireTokenRedirect) to start an interactive request.

调用 acquireTokenPopup 会弹出一个登录窗口。Calling acquireTokenPopup results in a pop-up sign-in window. 另外,acquireTokenRedirect 会将用户重定向到 Microsoft 标识平台终结点。Alternatively, acquireTokenRedirect redirects users to the Microsoft identity platform endpoint. 在该窗口中,用户需要确认其凭据,许可访问所需的资源,或完成双重身份验证。In that window, users need to confirm their credentials, give consent to the required resource, or complete two-factor authentication.

  const requestObj = {
      scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"]
  };

  this.authService.acquireTokenPopup(requestObj).then(function (tokenResponse) {
      // Callback code here
      console.log(tokenResponse.accessToken);
  }).catch(function (error) {
      console.log(error);
  });

备注

本快速入门对 Microsoft Internet Explorer 使用 loginRedirectacquireTokenRedirect 方法,因为 Internet Explorer 浏览器处理弹出窗口时会出现一个已知问题This quickstart uses the loginRedirect and acquireTokenRedirect methods with Microsoft Internet Explorer because of a known issue related to the handling of pop-up windows by Internet Explorer.

注销Log out

添加以下代码来注销用户:Add the following code to log out a user:

logout() {
  this.authService.logout();
}

添加 UIAdd UI

有关如何使用 Angular Material 组件库添加 UI 的示例,请查看示例应用程序For an example of how to add UI by using the Angular Material component library, see the sample application.

测试代码Test your code

  1. 通过在命令行提示符下从应用程序文件夹运行以下命令,启动 Web 服务器来侦听端口:Start the web server to listen to the port by running the following commands at a command-line prompt from the application folder:

    npm install
    npm start
    
  2. 在浏览器中输入 http://localhost:4200http://localhost:{port} ,其中,port 是 Web 服务器正在侦听的端口。In your browser, enter http://localhost:4200 or http://localhost:{port}, where port is the port that your web server is listening on.

首次开始登录到应用程序时,系统会提示你授予其访问你的个人资料的权限,并允许其将你登录:The first time that you start to sign in to your application, you're prompted to grant it access to your profile and allow it to sign you in:

“请求的权限”窗口

添加范围和委托的权限Add scopes and delegated permissions

Microsoft Graph API 需要 user.read 作用域来读取用户的个人资料。The Microsoft Graph API requires the user.read scope to read a user's profile. 默认情况下,在注册门户上注册的每个应用程序中,都会自动添加此范围。By default, this scope is automatically added in every application that's registered on the registration portal. Microsoft Graph 的其他 API 以及后端服务器的自定义 API 可能需要其他作用域。Other APIs for Microsoft Graph, as well as custom APIs for your back-end server, might require additional scopes. 例如,Microsoft Graph API 需要使用 Calendars.Read 作用域才能列出用户的日历。For example, the Microsoft Graph API requires the Calendars.Read scope in order to list the user's calendars.

若要在应用程序上下文中访问用户的日历,请将 Calendars.Read 委派权限添加到应用程序注册信息。To access the user's calendars in the context of an application, add the Calendars.Read delegated permission to the application registration information. 然后,将 Calendars.Read 作用域添加到 acquireTokenSilent 调用。Then, add the Calendars.Read scope to the acquireTokenSilent call.

备注

当你增加作用域数量时,可能会提示用户另外进行许可。The user might be prompted for additional consents as you increase the number of scopes.

如果后端 API 不需要范围(不建议),则你可以将 clientId 用作调用中的范围来获取令牌。If a back-end API doesn't require a scope (not recommended), you can use clientId as the scope in the calls to acquire tokens.

帮助和支持Help and support

如果需要帮助、需要报告问题,或者需要详细了解支持选项,请参阅面向开发人员的帮助和支持If you need help, want to report an issue, or want to learn about your support options, see Help and support for developers.

后续步骤Next steps

在由多部分组成的文章系列中,深入了解 Microsoft 标识平台上的单页应用程序 (SPA) 开发。Delve deeper into single-page application (SPA) development on the Microsoft identity platform in our the multi-part article series.