创建使用单一登录的 ASP.NET Office 加载项

用户可以登录 Office,Office Web 加载项能够利用此登录进程,授权用户访问加载项和 Microsoft Graph,而无需要求用户再登录一次。 本文将指导你完成在加载项中启用单一登录 (SSO) 的过程。

此示例演示如何生成以下部分:

  • 提供Microsoft Excel、Word 或 PowerPoint 中加载的任务窗格的客户端代码。 客户端代码调用 Office JS API getAccessToken() 以获取 SSO 访问令牌以调用服务器端 REST API。
  • 使用 ASP.NET Core 提供单个 REST API /api/files的服务器端代码。 服务器端代码使用 适用于 .NET 的 Microsoft 身份验证库 (MSAL.NET) 用于所有令牌处理、身份验证和授权。

此示例使用 SSO 和代表 (OBO) 流来获取正确的访问令牌并调用Microsoft Graph API。 如果不熟悉此流的工作原理,请参阅 SSO 在运行时的工作原理 ,了解更多详细信息。

先决条件

  • Visual Studio 2019 或更高版本。

  • 配置 Visual Studio 时的 Office/SharePoint 开发 工作负荷。

  • Microsoft 365 订阅中,至少存储在 OneDrive for Business 上的一些文件和文件夹。

  • 支持 IdentityAPI 1.3 要求集 的 Microsoft 365 内部版本。 通过 Microsoft 365 开发人员计划,你可能有资格获得 Microsoft 365 E5 开发人员订阅,其中包括开发人员沙盒;有关详细信息,请参阅 常见问题解答开发人员沙盒包含一个Microsoft Azure 订阅,可在本文后面的步骤中使用该订阅进行应用注册。 如果需要,可以使用单独的Microsoft Azure 订阅进行应用注册。 在 Azure Microsoft 获取试用版订阅。

设置初学者项目

Office Add-in ASPNET SSO 处克隆或下载存储库。

注意

示例有两个版本。

  • Begin 文件夹是初学者项目。 未直接连接到 SSO 或授权的外接程序的 UI 和其他方面已经完成。 本文后续章节将引导你完成此过程。
  • Complete 文件夹包含相同的示例,已完成本文中的所有编码步骤。 若要使用已完成的版本,只需按照本文中的说明进行操作,但将“Begin”替换为“Complete”,并跳过编写 客户端代码编写服务器端代码部分。

将以下值用于后续应用注册步骤的占位符。

占位符
<add-in-name> Office-Add-in-ASPNET-SSO
<fully-qualified-domain-name> localhost:44355
Microsoft Graph 权限 profile、openid、Files.Read

向 Microsoft 标识平台注册加载项

需要在 Azure 中创建表示 Web 服务器的应用注册。 这将启用身份验证支持,以便可以在 JavaScript 中向客户端代码颁发适当的访问令牌。 此注册支持客户端中的 SSO,以及使用 Microsoft 身份验证库 (MSAL) 的回退身份验证。

  1. 使用 Microsoft 365 租户的管理员凭据登录到 Azure 门户。 例如,MyName@contoso.onmicrosoft.com

  2. 选择“应用注册”。 如果未看到图标,请在搜索栏中搜索“应用注册”。

    Azure 门户主页。

    将显示 应用注册 页。

  3. 选择“新注册”

    “应用注册”窗格中的新注册。

    将显示注册应用程序页。

  4. 在“注册应用”页上,按如下方式设置值。

    • 将“名称”设置为“<add-in-name>”。
    • 将“ 支持的帐户类型 ”设置为 “任何组织目录中的帐户 (任何 Azure AD 目录 - 多租户) 和个人Microsoft帐户 (例如 Skype、Xbox)
    • 重定向 URI 设置为使用平台 单页应用程序 (SPA) ,并将 URI 设置为 https://<fully-qualified-domain-name>/dialog.html

    注册一个应用程序窗格,其中完成了名称和支持的帐户。

  5. 选择“注册”。 显示一条消息,指出已创建应用程序注册。

    指示已创建应用程序注册的消息。

  6. 复制并保存 应用程序 (客户端) ID目录 (租户) ID 的值。 你将在后面的过程中使用它们。

    显示客户端 ID 和目录 ID 的 Contoso 的应用注册窗格。

添加客户端密码

有时称为 应用程序密码,客户端密码是一个字符串值,你的应用可以使用它来代替证书来标识自身。

  1. 在左窗格中,选择“ 证书 & 机密”。 然后在“ 客户端机密 ”选项卡上,选择“ 新建客户端密码”。

    “证书 & 机密”窗格。

    此时会显示 “添加客户端机密 ”窗格。

  2. 添加客户端密码的说明。

  3. 选择机密的过期时间或指定自定义生存期。

    • 客户端机密生存期限制为两年 (24 个月) 或更短。 不能指定超过 24 个月的自定义生存期。
    • Microsoft建议将过期值设置为小于 12 个月。

    添加客户端密码窗格,说明和过期已完成。

  4. 选择“添加”。 将创建新机密并临时显示值。

重要

记录要在 客户端应用程序代码中使用的机密值。 离开此窗格后 ,永远不会再次显示 此机密值。

公开 Web API

  1. 在左窗格中,选择“ 公开 API”。

    此时会显示 “公开 API ”窗格。

    应用注册的“公开 API”窗格。

  2. 选择“ 设置 ”以生成应用程序 ID URI。

    应用注册的“公开 API”窗格中的“设置”按钮。

    将显示用于设置应用程序 ID URI 的部分,其中以 格式 api://<app-id>显示生成的应用程序 ID URI。

  3. 将应用程序 ID URI 更新为 api://<fully-qualified-domain-name>/<app-id>

    编辑“应用 ID URI”窗格,并将 localhost 端口设置为 44355。

    • 应用程序 ID URI 以格式 api://<app-id> 预填充应用 ID (GUID)。
    • 应用程序 ID URI 格式应为: api://<fully-qualified-domain-name>/<app-id>
    • 插入 介于 fully-qualified-domain-nameapi://<app-id> (这是 GUID) 。 例如,api://contoso.com/<app-id>
    • 如果使用 localhost,则格式应为 api://localhost:<port>/<app-id>。 例如,api://localhost:3000/c6c1f32b-5e55-4997-881a-753cc1d563b7

    有关其他应用程序 ID URI 的详细信息,请参阅 应用程序清单 identifierUris 属性

    注意

    如果收到一条错误,指出域已有所有者,但你拥有该域,请按照快速入门: 将自定义域名添加到 Azure Active Directory 中的步骤进行操作来注册该域,然后重复此步骤。 (如果未使用 Microsoft 365 租户中的管理员凭据登录,也可能会出现此错误。请参阅步骤 2。注销并使用管理员凭据再次登录,并重复步骤 3.)

添加范围

  1. “公开 API ”页上,选择“ 添加范围”。

    选择“添加范围”按钮。

    此时会打开 “添加范围 ”窗格。

  2. “添加范围 ”窗格中,指定作用域的属性。 下表显示了 需要 、、 openidFiles.ReadWriteMail.Read 权限的 profile和 Outlook 外接程序的示例值。 修改文本以匹配外接程序所需的权限。

    字段 说明
    范围名称 范围的名称。 常见的范围命名约定是 resource.operation.constraint 对于 SSO,必须将其设置为 access_as_user
    谁可以同意 确定是否需要管理员同意,或者用户是否可以在未经管理员批准的情况下同意。 为了学习 SSO 和示例,建议将其设置为 管理员和用户

    对于更高特权的权限,请选择“ 仅管理员 ”。
    管理员同意显示名称 仅对管理员可见的范围用途的简短说明。 Read/write permissions to user files. Read permissions to user mail and profiles.
    管理员同意说明 由范围授予的权限的更详细说明,仅供管理员查看。 Allow Office to have read/write permissions to all user files and read permissions to all user mail. Office can call the app's web APIs as the current user.
    用户同意显示名称 范围用途的简短说明。 仅当将 “谁可以同意 ”设置为 “管理员和用户”时,才会向用户显示。 Read/write permissions to your files. Read permissions to your mail and profile.
    用户同意说明 范围授予的权限的更详细说明。 仅当将 “谁可以同意 ”设置为 “管理员和用户”时,才会向用户显示。 Allow Office to have read/write permissions to your files, and read permissions to your mail and profile.
  3. “状态 ”设置为 “已启用”,然后选择“ 添加范围”。

    将状态设置为“启用”,然后选择“添加范围”按钮。

    定义的新范围将显示在窗格中。

    “公开 API”窗格上显示的新范围。

    注意

    显示在文本字段正下方的“作用域名称”的域部分应自动与上一步骤中设置的“应用 ID URI”匹配,并将 /access_as_user 附加到末尾;例如,api://localhost:6789/c6c1f32b-5e55-4997-881a-753cc1d563b7/access_as_user

  4. 选择添加客户端应用程序

    选择“添加客户端应用程序”。

    此时会显示 “添加客户端应用程序 ”窗格。

  5. “客户端 ID” 中输入 ea5a67f6-b6f3-4338-b240-c655ddc3cc8e。 此值预授权所有Microsoft Office 应用程序终结点。 如果还希望在 Microsoft Teams 中使用 Office 时预先授权 Office,请添加 1fec8e78-bce4-4aaf-ab1b-5451cc387264 (Microsoft Teams 桌面和 Teams 移动) ,并在 5e3ce6c0-2b1f-4285-8d4b-75ee78787346 Web) 上 (Teams。

    注意

    ea5a67f6-b6f3-4338-b240-c655ddc3cc8e ID 在以下所有平台上预授权 Office。 或者,如果出于任何原因想要在某些平台上拒绝对 Office 的授权,则可以输入以下 ID 的正确子集。 如果这样做,请省略要从中扣留授权的平台的 ID。 这些平台上加载项的用户将无法调用 Web API,但外接程序中的其他功能仍可正常工作。

    • d3590ed6-52b3-4102-aeff-aad2292ab01c (Microsoft Office)
    • 93d53678-613d-4013-afc1-62e9e444a0a5(Office 网页版)
    • bc59ab01-8403-45c6-8796-ac3ef710b3e3(Outlook 网页版)
  6. “授权范围”中,选中复选框 api://<fully-qualified-domain-name>/<app-id>/access_as_user

  7. 选择“添加应用程序”

    “添加客户端应用程序”窗格。

添加 Microsoft Graph 权限

  1. 在左窗格中,选择“ API 权限”。

    “API 权限”窗格。

    API 权限 ”窗格随即打开。

  2. 选择“添加权限”。

    在“API 权限”窗格中添加权限。

    此时会打开 “请求 API 权限 ”窗格。

  3. 选择 Microsoft Graph

    带有“Microsoft图形”按钮的“请求 API 权限”窗格。

  4. 选择“委托的权限”。

    具有委托权限按钮的“请求 API 权限”窗格。

  5. “选择权限” 搜索框中,搜索外接程序所需的权限。 例如,对于 Outlook 外接程序,可以使用 profileopenidFiles.ReadWriteMail.Read

    注意

    User.Read 权限可能已默认列出。 最好只请求所需的权限,因此,如果加载项实际上不需要此权限,我们建议取消选中此权限框。

  6. 选中每个权限显示的复选框。 请注意,在选择每个权限时,这些权限不会在列表中保持可见。 选择加载项所需的权限后,选择“ 添加权限”。

    “请求 API 权限”窗格,其中选择了一些权限。

  7. 选择“ 授予管理员同意[租户名称]”。 对于显示的确认,请选择“ ”。

配置访问令牌版本

必须定义应用可接受的访问令牌版本。 此配置是在 Azure Active Directory 应用程序清单中进行的。

定义访问令牌版本

如果你在任何组织目录中选择帐户类型以外的帐户类型 (任何 Azure AD 目录 - 多租户) 和个人Microsoft帐户 (,例如Skype、Xbox) ,则访问令牌版本可能会更改。 使用以下步骤确保访问令牌版本适用于 Office SSO 用法。

  1. 在左窗格中,选择“ 清单”。

    选择“Azure 清单”。

    此时会显示 Azure Active Directory 应用程序清单。

  2. 输入 2 作为 accessTokenAcceptedVersion 属性的值。

    接受访问令牌版本的值。

  3. 选择“保存”

    浏览器上弹出一条消息,指出清单已成功更新。

    清单更新的消息。

祝贺你! 你已完成应用注册,以便为 Office 加载项启用 SSO。

配置解决方案

  1. “开始” 文件夹的根目录中,在 Visual Studio 中打开解决方案 (.sln) 文件。 右键单击“ (”或选择并按住) “ 解决方案资源管理器” 中的顶部节点 (“解决方案”节点,而不是) 任一项目节点,然后选择“ 设置启动项目”。

  2. 在“通用属性”下,选择“启动项目”,然后选择“多个启动项目”。 确保这两个项目的 “操作” 设置为 “开始”,并且首先列出了 Office-Add-in-ASPNETCoreWebAPI 项目。 关闭该对话框。

  3. 解决方案资源管理器中,选择 Office-Add-in-ASPNET-SSO-manifest 项目,并打开外接程序清单文件“Office-Add-in-ASPNET-SSO.xml”,然后滚动到该文件的底部。 在结束 </VersionOverrides> 标记的正上方,你将找到以下标记。

    <WebApplicationInfo>
         <Id>Enter_client_ID_here</Id>
     	<Resource>api://localhost:44355/Enter_client_ID_here</Resource>
     	<Scopes>
            <Scope>Files.Read</Scope>
     		<Scope>profile</Scope>
            <Scope>openid</Scope>
     	</Scopes>
     </WebApplicationInfo>
    
  4. 将标记中两个位置的占位符“Enter_client_ID_here”替换为创建 Office-Add-in-ASPNET-SSO 应用注册时复制的应用程序 ID。 此 ID 与在 appsettings.json 文件中用于应用程序 ID 的 ID 相同。

    注意

    “资源”<>值是在注册外接程序时设置的应用程序 ID URI。 如果加载项是通过 AppSource 销售的,则 <“作用域> ”部分仅用于生成同意对话框。

  5. 保存并关闭清单文件。

  6. “解决方案资源管理器”中,选择 “Office-Add-in-ASPNET-SSO-web ”项目并打开 appsettings.json 文件。

  7. 将占位符 Enter_client_id_here 替换为之前保存 的应用程序 (客户端) ID 值。

  8. 将占位符 Enter_client_secret_here 替换为之前保存的客户端机密值。

    注意

    如果为单租户配置了应用注册,则还必须更改 TenantId 以支持单租户。 将 Common 值替换为单租户支持的 Application (客户端) ID

  9. 保存并关闭appsettings.json文件。

编写客户端代码

获取访问令牌并调用应用程序服务器 REST API

  1. Office-Add-in-ASPNETCore-WebAPI 项目中,打开 wwwroot\js\HomeES6.js 文件。 它已有确保 Promises 受支持的代码,即使在 Trident (Internet Explorer 11) Webview 控件中,也 Office.onReady 调用了将处理程序分配给外接程序的唯一按钮。

    注意

    顾名思义,HomeES6.js 使用 JavaScript ES6 语法,因为使用 asyncawait 最好地显示了 SSO API 的基本简单性。 当 localhost 服务器启动时,此文件将转译为 ES5 语法,以便示例支持 Trident。

  2. getUserFileNames 函数中,将 TODO 1 替换为下列代码。 关于此代码,请注意以下几点:

    • 它调用 Office.auth.getAccessToken 以使用 SSO 从 Office 获取访问令牌。 此令牌将包含用户的标识以及对应用程序服务器的访问权限。
    • 访问令牌被 callRESTApi 传递到 ,该令牌对应用程序服务器进行实际调用。 然后,应用程序服务器使用 OBO 流调用 Microsoft Graph。
    • 调用 getAccessToken 时出现的任何错误都将由 handleClientSideErrors处理。
       let fileNameList = null;
    try {
        let accessToken = await Office.auth.getAccessToken(options);
        fileNameList = await callRESTApi("/api/files", accessToken);
    }
    catch (exception) {
        if (exception.code) {
            handleClientSideErrors(exception);
        }
        else {
            showMessage("EXCEPTION: " + exception);
        }
    }
    
    
  3. getUserFileNames 函数中,将 TODO 2 替换为下列代码。 这会将文件名列表写入文档。

     try {
         await writeFileNamesToOfficeDocument(fileNameList);
         showMessage("Your data has been added to the document.");
     } catch (error) {
         // The error from writeFileNamesToOfficeDocument will begin 
         // "Unable to add filenames to document."
         showMessage(error);
     }
    
  4. callRESTApi 函数中,将 TODO 3 替换为下列代码。 关于此代码,请注意以下几点:

    • 它构造包含访问令牌的授权标头。 这向应用程序服务器确认此客户端代码有权访问 REST API。
    • 它请求 JSON 返回类型,以便所有返回值都以 JSON 处理。
    • 任何错误都会传递给 handleServerSideErrors 进行处理。
     try {
         let result = await $.ajax({
             url: relativeUrl,
             headers: { "Authorization": "Bearer " + accessToken },
             type: "GET",
             dataType: "json",
             contentType: "application/json; charset=utf-8"
         });
         return result;
     } catch (error) {
         handleServerSideErrors(error);
     }
    

处理 SSO 错误和应用程序 REST API 错误

  1. handleSSOErrors 函数中,将 TODO 4 替换为下列代码。 有关这些错误的详细信息,请参阅对 Office 加载项中的 SSO 进行故障排除

     switch (error.code) {
         case 13001:
             // No one is signed into Office. If the add-in cannot be effectively used when no one 
             // is logged into Office, then the first call of getAccessToken should pass the 
             // `allowSignInPrompt: true` option.
             showMessage("No one is signed into Office. But you can use many of the add-ins functions anyway. If you want to log in, press the Get OneDrive File Names button again.");
             break;
         case 13002:
             // The user aborted the consent prompt. If the add-in cannot be effectively used when consent
             // has not been granted, then the first call of getAccessToken should pass the `allowConsentPrompt: true` option.
             showMessage("You can use many of the add-ins functions even though you have not granted consent. If you want to grant consent, press the Get OneDrive File Names button again.");
             break;
         case 13006:
             // Only seen in Office on the web.
             showMessage("Office on the web is experiencing a problem. Please sign out of Office, close the browser, and then start again.");
             break;
         case 13008:
             // Only seen in Office on the web.
             showMessage("Office is still working on the last operation. When it completes, try this operation again.");
             break;
         case 13010:
             // Only seen in Office on the web.
             showMessage("Follow the instructions to change your browser's zone configuration.");
             break;
         default:
             // For all other errors, including 13000, 13003, 13005, 13007, 13012, and 50001, fall back
             // to non-SSO sign-in by using MSAL authentication.
             showMessage("SSO failed. In these cases you should implement a falback to MSAL authentication.");
             break;
     }
    
  2. handleServerSideErrors 函数中,将 TODO 5 替换为下列代码。

    // Check headers to see if admin has not consented.
    const header = errorResponse.getResponseHeader('WWW-Authenticate');
    if (header !== null && header.includes('proposedAction=\"consent\"')) {
        showMessage("MSAL ERROR: " + "Admin consent required. Be sure admin consent is granted on all scopes in the Azure app registration.");
        return;
    }
    
    
  3. handleServerSideErrors 函数中,将 TODO 6 替换为下列代码。 关于此代码,请注意以下几点:

    • 在某些情况下,需要额外的同意,例如 2FA。 Microsoft标识返回完成同意所需的其他声明。 此代码使用其他声明添加 authChallenge 属性并再次调用 getUserfileNames 。 使用其他声明再次调用 时 getAccessToken ,用户会收到所有必需形式的身份验证的提示。
    // Check if Microsoft Graph requires an additional form of authentication. Have the Office host 
    // get a new token using the Claims string, which tells Microsoft identity to prompt the user for all 
    // required forms of authentication.
    const errorDetails = JSON.parse(errorResponse.responseJSON.value.details);
    if (errorDetails) {
        if (errorDetails.error.message.includes("AADSTS50076")) {
            const claims = errorDetails.message.Claims;
            const claimsAsString = JSON.stringify(claims);
            getUserFileNames({ authChallenge: claimsAsString });
            return;
        }
    }
    
  4. handleServerSideErrors 函数中,将 TODO 7 替换为下列代码。 关于此代码,请注意以下几点:

    • 在极少数情况下,原始 SSO 令牌已过期,它将检测到此错误情况并再次调用 getUserFilenames 。 这会导致另一个调用,该调用将 getAccessToken 返回刷新的访问令牌。 变量 retryGetAccessToken 对重试次数进行计数,目前配置为仅重试一次。
    • 最后,如果无法处理错误,则默认是在任务窗格中显示错误。
    // Results from other errors (other than AADSTS50076) will have an ExceptionMessage property.
    const exceptionMessage = JSON.parse(errorResponse.responseText).ExceptionMessage;
    if (exceptionMessage) {
        // On rare occasions the access token is unexpired when Office validates it,
        // but expires by the time it is sent to Microsoft identity in the OBO flow. Microsoft identity will respond
        // with "The provided value for the 'assertion' is not valid. The assertion has expired."
        // Retry the call of getAccessToken (no more than once). This time Office will return a 
        // new unexpired access token.
        if ((exceptionMessage.includes("AADSTS500133"))
            && (retryGetAccessToken <= 0)) {
            retryGetAccessToken++;
            getUserFileNames();
            return;
        }
        else {
            showMessage("MSAL error from application server: " + JSON.stringify(exceptionMessage));
            return;
        }
    }
    // Default error handling if previous checks didn't apply.
    showMessage(errorResponse.responseJSON.value);
    
  5. 保存文件。

编写服务器端代码

服务器端代码是一个 ASP.NET 核心服务器,它为客户端提供 REST API 以供调用。 例如,REST API /api/files 从用户的 OneDrive 文件夹中获取文件名列表。 每个 REST API 调用都需要客户端提供访问令牌,以确保正确的客户端正在访问其数据。 访问令牌通过代表流 (OBO) 交换Microsoft Graph 令牌。 新的 Microsoft Graph 令牌由 MSAL.NET 库缓存,用于后续 API 调用。 它永远不会在服务器端代码之外发送。 Microsoft标识文档将此服务器称为中间层服务器,因为它位于从客户端代码到Microsoft服务的流程中。 有关详细信息,请参阅 中间层访问令牌请求

配置 Microsoft Graph 和 OBO 流

  1. 打开 文件, Program.cs 并将 替换为 TODO 8 以下代码。 关于此代码,请注意以下几点:

    • 它添加所需的服务来处理 REST API 所需的令牌验证。
    • 它在调用 EnableTokenAcquisitionToCallDownstreamApi().AddMicrosoftGraph(...)中添加Microsoft Graph 和 OBO 流支持。 OBO 流会自动处理,Microsoft Graph SDK 会提供给 REST API 控制器。
    • appsettings.json 文件中指定了 DownstreamApi 配置。
    // Add services to the container.
    builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration)
                    .EnableTokenAcquisitionToCallDownstreamApi()
                        .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
                        .AddInMemoryTokenCaches();
    
    

创建 /api/filenames REST API

  1. Controllers 文件夹中,打开 FilesController.cs 文件。 将 替换为 TODO 9 以下代码。 关于此代码,请注意以下几点:

    • 它指定 [Authorize] 属性,以确保每次调用类中 FilesController 任何 REST API 时都验证访问令牌。 有关详细信息,请参阅 验证令牌
    • 它指定 [RequiredScope("access_as_user")] 属性以确保客户端在访问令牌中具有正确的 access_as_user 范围。
    • 构造函数初始化 对象, _graphServiceClient 以便更轻松地调用 Microsoft Graph REST API。
    [Authorize]
    [Route("api/[controller]")]
    [RequiredScope("access_as_user")]
    public class FilesController : Controller
    {        
        public FilesController(ITokenAcquisition tokenAcquisition, GraphServiceClient graphServiceClient, IOptions<MicrosoftGraphOptions> graphOptions)
        {
            _tokenAcquisition = tokenAcquisition;
            _graphServiceClient = graphServiceClient;
            _graphOptions = graphOptions;
    
        }
    
        private readonly ITokenAcquisition _tokenAcquisition;
        private readonly GraphServiceClient _graphServiceClient;
        private readonly IOptions<MicrosoftGraphOptions> _graphOptions;
    
        // TODO 10: Add the REST API to get filenames.
    
    }
    
  2. TODO 10 替换为下面的代码。 关于此代码,请注意以下几点:

    • 它创建 /api/files REST API。
    • 它通过 类处理来自 MSAL 的 MsalException 异常。
    • 它通过 类处理Microsoft图形 API 调用的 ServiceException 异常。
     // GET api/files
        [HttpGet]
        [Produces("application/json")]
        public async Task<IActionResult> Get()
        {
            List<DriveItem> result = new List<DriveItem>();
            try
            {
                var files = await _graphServiceClient.Me.Drive.Root.Children.Request()
                    .Top(10)
                    .Select(m => new { m.Name })
                    .GetAsync();
    
                result = files.ToList();
            }
            catch (MsalException ex)
            {
                var errorResponse = new
                {
                    message = "An authentication error occurred while acquiring a token for downstream API",
                    details = ex.Message
                };
    
                return StatusCode((int)HttpStatusCode.Unauthorized, Json(errorResponse));
            }
            catch (ServiceException ex)
            {
                if (ex.InnerException is MicrosoftIdentityWebChallengeUserException challengeException)
                {
                    _tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeader(_graphOptions.Value.Scopes.Split(' '),
                        challengeException.MsalUiRequiredException);
                }
                else
                {
                    var errorResponse = new
                    {
                        message = "An error occurred calling Microsoft Graph",
                        details = ex.RawResponseBody
                    };
                    return StatusCode((int)HttpStatusCode.BadRequest, Json(errorResponse));
                }
            }
            catch (Exception ex)
            {
                var errorResponse = new
                {
                    message = "An error occurred while calling the downstream API",
                    details = ex.Message
                };
                return StatusCode((int)HttpStatusCode.BadRequest, Json(errorResponse));
    
            }
            return Json(result);
        }
    

运行解决方案

  1. 在 Visual Studio 的“ 生成 ”菜单上,选择“ 清理解决方案”。 完成后,再次打开“生成”菜单,并选择“生成解决方案”。

  2. “解决方案资源管理器”中,选择 “Office-Add-in-ASPNET-SSO-manifest ”项目节点。

  3. 在“属性”窗格中,打开“启动文档”下拉列表,然后选择三个选项之一(“Excel”、“Word”或“PowerPoint”)。

    选择所需的 Office 客户端应用程序:Excel、PowerPoint 或 Word。

  4. 按 F5。 或选择 “调试 > 启动调试”。

  5. 在 Office 应用程序中,选择“在 SSO ASP.NET 组中显示外接程序”以打开任务窗格加载项。

  6. 选择“ 获取 OneDrive 文件名”。 如果使用 Microsoft 365 教育版或工作帐户或Microsoft帐户登录 Office,并且 SSO 按预期工作,则任务窗格上会显示 OneDrive for Business 中的前 10 个文件和文件夹名称。 如果未登录,或者你处于不支持 SSO 的方案中,或者 SSO 由于任何原因而不起作用,系统会提示你登录。 登录后,将显示文件和文件夹名称。

部署外接程序

准备好部署到过渡服务器或生产服务器时,请务必更新项目解决方案中的以下区域。

  • appsettings.json 文件中,将 更改为过渡域名或生产域名。
  • 在整个项目中更新对 localhost:7080 的任何引用,以使用过渡或生产 URL。
  • 在 Azure 应用注册中更新对 localhost:7080 的任何引用,或创建用于过渡或生产的新注册。

有关详细信息,请参阅 托管和部署 ASP.NET Core

另请参阅