本文介绍如何从 SharePoint 框架解决方案连接到受 Azure Active Directory 保护的企业 API。 它既包括创建和保护 API,也包括构建 SharePoint 框架解决方案。

创建受 Azure AD 保护的企业 API

开始创建受 Azure Active Directory 保护的企业 API。 虽然没有限制如何从 SharePoint 框架的角度实现 API,但在本教程中,将使用 Azure Functions 构建 API,并使用 Azure 应用服务身份验证来保护它。

虽然你所在的组织很可能已经具有公开其应用程序的特定 API,但本章节的目的是让你对实现和配置步骤有一个完整的了解。

创建 Azure 函数

Azure 门户 中,新建一款 Function App。

有关在 Azure 中创建 Function App 的详细信息,请参阅 从 Azure 门户创建 Function App 帮助文章。

在 Function App 中,您可以新建 HTTP 触发的函数。 在此示例中,将使用 C# 生成函数,但是一般来说,在必须使用哪种编程语言方面没有任何限制。

在 Function App 中,按“新函数” 按钮:

从的模板列表中,选择“ HTTP 触发器 ”:

在“新函数”面板中,指定函数名称并将“授权级别” 设置为“匿名” ,然后按“创建” 按钮:

Azure Functions 可通过多种方式进行保护。 由于你想要使用 Azure AD 来保护函数,而不是保护函数本身,因此你将保护基础 Function App。 这就是为什么在此阶段,你不设置保护函数本身的理由。 应用于 Function App 的身份验证设置将应用于该应用内的所有函数。

在创建函数后,将其内容替换为下面的代码段:

#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
    log.LogInformation("C# HTTP trigger function processed a request.");
    return (ActionResult)new OkObjectResult(new List<object> {
        new {
            Id = 1,
            OrderDate = new DateTime(2016, 1, 6),
            Region = "east",
            Rep = "Jones",
            Item = "Pencil",
            Units = 95,
            UnitCost = 1.99,
            Total = 189.05
        new {
            Id = 2,
            OrderDate = new DateTime(2016, 1, 23),
            Region = "central",
            Rep = "Kivell",
            Item = "Binder",
            Units = 50,
            UnitCost = 19.99,
            Total = 999.50
        new {
            Id = 3,
            OrderDate = new DateTime(2016, 2, 9),
            Region = "central",
            Rep = "Jardine",
            Item = "Pencil",
            Units = 36,
            UnitCost = 4.99,
            Total = 179.64
        new {
            Id = 4,
            OrderDate = new DateTime(2016, 2, 26),
            Region = "central",
            Rep = "Gill",
            Item = "Pen",
            Units = 27,
            UnitCost = 19.99,
            Total = 539.73
        new {
            Id = 5,
            OrderDate = new DateTime(2016, 3, 15),
            Region = "west",
            Rep = "Sorvino",
            Item = "Pencil",
            Units = 56,
            UnitCost = 2.99,
            Total = 167.44

按“存并运行”按钮,验证该函数是否正常工作:

如果正确执行函数,则会看到状态:200 OK 标签,且测试窗格中会显示列表订单。

保护 Azure 函数

Azure 函数现已正常工作,下一步是使用 Azure Active Directory 对其进行保护,这样的话需要使用组织帐户登录才能访问。

在 Function App 边栏选项卡上,从侧面板中选择 Function App:

在上方区域,切换到“平台功能”选项卡:

接下来,从“网络”组中,选择“身份验证/授权”链接:

在“身份验证/授权”边栏选项卡上,通过将“应用服务身份验证”切换按钮切换至“开启”来启用应用服务身份验证:

在“请求未经验证时需执行的操作”下拉列表中,将值更改为“使用 Azure Active Directory 登录”。 此设置确保不允许向 API 发送匿名请求:

接下来,在身份验证提供程序列表中,选择“Azure Active Directory”

在“Azure Active Directory 设置”边栏选项卡上,设置“管理模式”选项为“快速”。 设置第二个“管理模式”选项为“新建 AD 应用”

在继续操作之前,记下“创建应用”字段中的值。 此值表示将用于保护 API 的 Azure AD 应用程序的名称,且稍后会用到,以请求权限访问 SharePoint 框架项目中的 API。

按“确定”按钮确认选择。

返回“身份验证/授权”边栏选项卡,按“保存”按钮更新该 Function App 的身份验证和授权设置:

通过以私人模式打开新浏览器窗口并导航到 API,确认已正确保护 API。 Function App 的 URL 可从 Function App 边栏选项卡的 “概述”部分找到。 如果已正确应用身份验证设置,应会重定向到 Azure AD 登录页面:

获取 Azure AD 应用程序 ID

如需请求访问令牌以连接到 API,则需要用于保护该 API 的 Azure AD 应用程序的应用程序 ID。

在 Function App 中,导航到“身份验证”设置。 如果“身份验证”链接在“配置功能”页眉下不可用,请按左面板 Function App 旁边的“刷新”按钮:

从身份验证提供程序列表中选择“Azure Active Directory”

在“Azure Active Directory 设置”边栏选项卡上,按“管理应用程序”按钮:

在 Azure AD 应用程序边栏选项卡上,复制“应用程序 ID”属性的值:

启用 CORS

将从 SharePoint 页面上运行的 JavaScript 中调用函数应用。 由于 API 托管在与 SharePoint 门户不同的域中,跨域安全约束将应用于 API 调用。 默认情况下,使用 Azure Function App 实现的 API 不能从其他域调用。 可以通过调整 Function App 的 CORS 设置来更改。

如果您正在使用 SharePoint Online cookie 而不是通过 OAuth API 来验证,则无法通过 Azure 门户来配置 CORS 设置。 若要完成身份验证,必须清除 Azure 门户中所有 CORS 设置,并在 API 中指定它们。

在 Function App 中,切换到“平台功能”选项卡。

从“API”组中,选择“CORS”链接:

在允许的源列表中,添加 SharePoint 租户的 URL,例如https://contoso.sharepoint.com:

按“保存”按钮确认更改。

从 SharePoint 框架使用受 Azure AD 保护的企业 API

在配置 API 且正常工作后,下一步是构建将使用此 API 的 SharePoint 框架解决方案。

在继续之前,请确保已安装 SharePoint 框架 Yeoman 生成器版本 1.4.1 或更高版本。 如果已全局安装生成器,则可以通过在命令行中执行来查看已安装的版本:npm ls -g --depth=0

新建 SharePoint 框架项目

接下来,将创建可使用 API 的新 SharePoint Framework 项目。

在命令行中,为项目新建文件夹:

md contoso-api

更改工作目录:

cd contoso-api

要创建新项目,启动 SharePoint 框架 Yeoman 生成器:

yo @microsoft/sharepoint

出现提示时,使用以下值:

  • 解决方案名称是什么? contoso-api
  • 你想要为你的组件设定哪些基准包? 仅 SharePoint Online(最新)
  • 要将文件存放在哪里? 使用当前文件夹
  • 是否要允许租户管理员选择能够立即将解决方案部署到所有站点的选项,而无需运行任何站点中的部署或添加应用程序功能?
  • 解决方案中的组件是否需要访问唯一且不与租户中的其他组件共享的 Web API 的权限?
  • 要创建哪种类型的客户端组件? WebPart
  • Web 部件名称是什么? 订单
  • Web 部件说明是什么? 显示最近的订单
  • 要使用哪种框架? 没有 JavaScript 框架
  • 创建项目后,请在代码编辑器中打开。 在本教程中,你将使用 Visual Studio Code:

    请求对企业 API 的权限

    默认情况下,SharePoint 框架无权访问企业 API,即使它们在相同的 Azure Active Directory 中注册为 Office 365。 这是设计使然,并使组织能够理智地选择应向部署到 SharePoint 的脚本和客户端解决方案公开的 API。 要获得对企业 API 的访问权限,需要从正在构建的 SharePoint 框架项目中发出权限请求。

    在代码编辑器中,打开“config/package-solution.json”文件:

    solution属性中,添加一个名为 webApiPermissionRequests 的新属性,同时引用用于保护 API 的 Azure AD 应用程序:

    "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json", "solution": { "name": "contoso-api-client-side-solution", "id": "8cbc01fb-bab6-48fc-afec-2c2053759771", "version": "1.0.0.0", "includeClientSideAssets": true, "skipFeatureDeployment": true, "isDomainIsolated": false, "webApiPermissionRequests": [ "resource": "contoso-api-dp20191109", "scope": "user_impersonation" "paths": { "zippedPackage": "solution/contoso-api.sppkg"

    resource属性值必定是指用于保护 API 的 Azure AD 应用程序的名称。 scope属性的值指定解决方案需要的权限范围,以便与 API 进行通信。 在本教程中,Azure AD 仅用于保护 API,因此 user_impersonation 是要使用的范围。

    如果要连接以前创建的企业 API,请联系管理员,以便为你提供用于保护此 API 的 Azure AD 应用程序的详细信息。 将需要一些信息,如应用程序 ID、应用程序公开的权限以及配置的受众。

    连接到企业 API

    最后一部分是实现与企业 API 的实际连接。

    在代码编辑器中,打开“src\webparts\orders\OrdersWebPart.ts”文件:

    在该文件的上方区域,通过添加下面的代码段引用 AadHttpClientHttpClientResponse 类:

    import { AadHttpClient, HttpClientResponse } from '@microsoft/sp-http';
    

    对于 OrdersWebPart类,请添加一个名为 ordersClient 的新类变量:

    export default class OrdersWebPart extends BaseClientSideWebPart<IOrdersWebPartProps> {
      private ordersClient: AadHttpClient;
      // shortened for brevity
    

    接下来,在OrdersWebPart类中,替代onInit()方法以创建 AadHttpClient 实例:

    export default class OrdersWebPart extends BaseClientSideWebPart<IOrdersWebPartProps> {
      private ordersClient: AadHttpClient;
      protected onInit(): Promise<void> {
        return new Promise<void>((resolve: () => void, reject: (error: any) => void): void => {
          this.context.aadHttpClientFactory
            .getClient('6bc8bca8-5866-405d-b236-9200bdbb73c0')
            .then((client: AadHttpClient): void => {
              this.ordersClient = client;
              resolve();
            }, err => reject(err));
      // shortened for brevity
    

    作为“AadHttpClient”AadHttpClient构造函数第二个参数传递的 GUID 是用于保护企业 API 的 Azure AD 应用程序的应用程序 ID。

    最后,扩展render()方法以加载并显示从企业 API 检索到的订单:

    export default class OrdersWebPart extends BaseClientSideWebPart<IOrdersWebPartProps> {
      private ordersClient: AadHttpClient;
      protected onInit(): Promise<void> {
        return new Promise<void>((resolve: () => void, reject: (error: any) => void): void => {
          this.context.aadHttpClientFactory
            .getClient('6bc8bca8-5866-405d-b236-9200bdbb73c0')
            .then((client: AadHttpClient): void => {
              this.ordersClient = client;
              resolve();
            }, err => reject(err));
      public render(): void {
        this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'orders');
        this.ordersClient
          .get('https://contoso-api-dp20191109.azurewebsites.net/api/Orders', AadHttpClient.configurations.v1)
          .then((res: HttpClientResponse): Promise<any> => {
            return res.json();
          .then((orders: any): void => {
            this.context.statusRenderer.clearLoadingIndicator(this.domElement);
            this.domElement.innerHTML = `
              <div class="${ styles.orders}">
                <div class="${ styles.container}">
                  <div class="${ styles.row}">
                    <div class="${ styles.column}">
                      <span class="${ styles.title}">Orders</span>
                      <p class="${ styles.description}">
                          ${orders.map(o => `<li>${o.rep} $${o.total}</li>`).join('')}
                      <a href="https://aka.ms/spfx" class="${ styles.button}">
                        <span class="${ styles.label}">Learn more</span>
              </div>`;
          }, (err: any): void => {
            this.context.statusRenderer.renderError(this.domElement, err);
      // shortened for brevity
    

    将解决方案部署到 SharePoint 应用程序目录

    在完成实施 SharePoint 框架解决方案后,下一步是将其部署到 SharePoint。

    首先,使用命令行来构建和打包项目:

    gulp bundle --ship && gulp package-solution --ship
    

    接下来,在 Explorer 中打开项目文件夹,并导航到“sharepoint/solution”文件夹:

    在 Web 浏览器中,导航到 Office 365 租户的租户应用程序目录:

    通过从 Explorer 拖放到租户应用程序目录,添加新生成的 .sppkg 文件:

    出现提示时,勾选“让此解决方案可供组织中的所有网站使用”复选框。 此外,请注意注释,指示应转到“服务主体权限管理页”以批准待定权限请求。 按“部署”按钮确认部署。

    授予对企业 API 的访问权限

    在 Web 浏览器中,通过从“Office 365 应用启动器”中选择“管理员”选项,导航到租户管理员网站:

    在菜单中,选择“管理中心”组中的“SharePoint”

    在 SharePoint 管理中心,使用“尝试新 SharePoint 管理中心预览”链接导航到“新 SharePoint 管理中心预览”:

    在新管理中心,从菜单中选择“API 管理”选项:

    在 API 管理页的“等待审批”组中,选择新添加的权限请求以访问 contoso-api API(将显示您的 API 名称):

    接下来,从工具栏中按“批准或拒绝”按钮:

    在侧面板中,通过按“批准”按钮,授予对 API 的访问权限:

    可以创建连接到使用 AAD 保护的 API 的域隔离 Web 部件。 在这种情况下,必须对 API 的 CORS 进行适当的重新配置,因为域隔离 Web 部件的每个实例都在具有唯一域的专用应用程序 Web 中执行。

    向页面添加 Orders Web 部件

    若要确保所有内容都按预期方式工作,请向页面添加之前创建的 Orders Web 部件。

    在 Web 浏览器中,导航到租户中的一个站点。 从工具栏上,选择“编辑”选项:

    在画布上,选择要向其添加 Web 部件的分区:

    选择 + 选项以打开工具箱。 在搜索框中键入 订单 以快速查找 订单 Web 部件:

    选择要添加到页面的“Orders” Web 部件。 你会看到从企业 API 中检索到的订单列表:

    如果收到"无法打开弹出窗口"的技术详细信息错误,则说明启用了弹出窗口阻止程序。 需要禁用该网站浏览器弹出窗口阻止程序,才能正确显示的页面。