通过 Entra ID 应用程序权限授予访问权限

使用 SharePoint Online 时,可以在 Entra ID 中定义应用程序,并且可以向这些应用程序授予对 SharePoint 的权限,也可以授予对 Microsoft 365 中所有其他服务的权限。 如果使用的是 SharePoint Online,则此模型是首选模型,如果在本地使用 SharePoint,则必须通过基于的 Azure ACS 使用 SharePoint Only 模型,如此所述。

设置 Entra ID 应用以仅应用访问

在 Entra ID 中,当执行仅限应用时,必须使用证书来请求对 SharePoint CSOM/REST API 的访问:拥有证书及其私钥的任何人都可以使用该应用和授予应用程序的权限。 下面逐步介绍了如何设置此模型。

使用 Entra 门户创建 Entra 应用程序

如果要手动创建 Entra 应用程序,请执行以下步骤来创建和配置 Entra 应用程序:

  1. 导航到 Entra 门户并单击“应用程序”,然后从左侧导航应用注册
  2. 单击“ 新建注册 ”页
  3. 为 Entra 应用程序提供 名称 (例如 Microsoft365AssessmentToolForWorkflow)
  4. 选择 “公共客户端/本机 (移动 & 桌面) ”,输入 http://localhost 作为重定向 URI
  5. 单击“ 注册 ”,创建并打开 Entra 应用程序
  6. 确保通过 API 权限链接左侧导航定义了所需的应用程序权限
  7. 转到 “证书 & 机密”,单击“ 证书 ”和“ 上传证书”,选择证书的.cer文件并将其添加。
  8. 单击“ 授予管理员同意...” 以同意添加的权限

注意

使用 PnP PowerShell 创建 Entra 应用程序

使用 PnP PowerShell 创建 Entra 应用程序变得非常简单。 Register-PnPAzureADApp cmdlet 将创建新的 Entra 应用程序,在“个人”节点 (= CurrentUser 证书存储的“我的) ”内创建新的自签名证书,并将该证书与创建的 Entra 应用程序挂钩。 最后,配置正确的权限,并提示你同意这些权限。

重要

如果在以下步骤中遇到错误,则可能没有所需的权限。 请联系租户/Entra 管理员寻求帮助。

# Before you use this!! 
#  - Remove/update the application/delegated permissions depending on your needs
#  - Also update the Tenant and Username properties to match your environment.
#
# If you prefer to have a password set to secure the created PFX file then add below parameter
# -CertificatePassword (ConvertTo-SecureString -String "password" -AsPlainText -Force)
#
# See https://pnp.github.io/powershell/cmdlets/Register-PnPAzureADApp.html for more options
#
Register-PnPAzureADApp -ApplicationName Microsoft365AssessmentToolForAlerts `
                       -Tenant contoso.onmicrosoft.com `
                       -Store CurrentUser `
                       -GraphApplicationPermissions "Sites.Read.All" `
                       -SharePointApplicationPermissions "Sites.FullControl.All" `
                       -GraphDelegatePermissions "Sites.Read.All", "User.Read" `
                       -SharePointDelegatePermissions "AllSites.FullControl" `
                       -Username "joe@contoso.onmicrosoft.com" `
                       -Interactive

注意

将 替换为 contoso.onmicrosoft.com Entra 租户名称,并确保将 替换为 joe@contoso.onmicrosoft.com 作为 Entra 管理员 (或全局管理员) 的用户 ID。 如果不确定 Entra 租户名称,请转到 https://entra.microsoft.com/#view/Microsoft_AAD_IAM/TenantOverview.ReactView 并检查主域的值。

在上述命令上按 Enter 后,系统会提示登录,并且应使用为 Username 参数指定的用户登录。 完成后,将创建并配置 Entra 应用程序,然后等待 60 秒,以确保创建已传播到所有系统。 最后一步是管理员同意流:系统将再次提示你使用指定的管理员用户登录,然后是一个同意对话框,其中显示了正在授予应用程序的权限。 按 “接受” 完成同意流。 在生成的输出中,你将获得一些关键信息:

Pfx file               : D:\assessment\Microsoft365AssessmentTool.pfx
Cer file               : D:\assessment\Microsoft365AssessmentTool.cer
AzureAppId/ClientId    : 95610f5d-729a-4cd1-9ad7-1fa9052e50dd
Certificate Thumbprint : 165CCE93E08FD3CD85B7B25D5E91C05B1D1E49FE

Register-PnPAzureADApp运行 不仅创建了 Entra 应用程序并配置了 Entra 应用程序,而且还为应用程序权限流创建了证书。 此证书已添加到个人节点下的当前用户的证书存储中。 可以在 certmgr 命令行上使用 打开本地用户的证书存储。

注意

证书还会导出为文件系统上的 PFX 文件和 cer 文件,请随意删除这些导出的文件,因为从证书存储区使用证书更容易。

通过 PnP PowerShell 使用此主体

如果要将此 Entra ID 仅限应用程序主体与 PnP PowerShell 配合使用,请在安装 PnP PowerShell 模块后,可以使用 PnP PowerShell 支持 的非交互式身份验证 连接到 SharePoint Online 环境。

Connect-PnPOnline [yourtenant].sharepoint.com -ClientId <client id of your Entra ID Application Registration> -Tenant <tenant>.onmicrosoft.com -CertificatePath <path to your .pfx certificate> 

# or

Connect-PnPOnline [yourtenant].sharepoint.com -ClientId <client id of your Entra ID Application Registration> -Tenant <tenant>.onmicrosoft.com -Thumbprint <thumbprint that can be found in the certificate> 

现在可以通过 PnP PowerShell 使用此证书“仅应用”信任,对 SharePoint Online 环境执行操作。

注意

PnP PowerShell 是一种开放源代码解决方案,其中包含为其提供支持的活动社区。 没有用于 Microsoft 开放源代码工具支持的 SLA。

在 CSOM 应用程序中使用此主体

按照以下步骤生成一个 C# 控制台应用程序,该应用程序使用 CSOM 并使用 Entra ID 和应用程序权限进行身份验证:

  • 为 c# .net 创建新的基于控制台的解决方案
  • 通过在包管理器控制台中运行以下命令来安装 MSAL.NET NuGet 包: Install-Package Microsoft.Identity.Client
  • 将以下代码添加到应用程序,以配置 MSAL 并使用证书获取访问令牌
using System;
using System.Threading.Tasks;
using Microsoft.SharePoint.Client;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Identity.Client;

namespace YourNamespace
{
    class Program
    {
        static async Task Main(string[] args)
        {
            //Generate Access Token using MSAL
            #region Access Token
            string tenantId = "{Tenant_Id}";
            string clientId = "{Client_Id}";
            string certificatePath = @"{Full Path to the certificate .Pfx file}";
            string certificatePassword = "{Password of the .Pfx file}";
            var siteUrl = new Uri("{SPO Site URL}");

            X509Certificate2 certificate = new X509Certificate2(certificatePath, certificatePassword);

            IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithCertificate(certificate)
                .WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
                .Build();

            string[] scopes = new string[] { "https://domain.sharepoint.com/.default" };

            AuthenticationResult result = await app.AcquireTokenForClient(scopes).ExecuteAsync();

            var accessToken = result.AccessToken;

            Console.WriteLine($"Access Token: {accessToken}");
            Console.WriteLine();
            Console.WriteLine();
            #endregion

            //Generate SPO Context using the Access token
            #region SPO Context
            using (var context = new ClientContext(siteUrl))
            {
                context.ExecutingWebRequest += async (sender, e) =>
                {
                    // Insert the access token in the request
                    e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accessToken;
                };

                // Read web properties
                var web = context.Web;
                context.Load(web, w => w.Id, w => w.Title);
                await context.ExecuteQueryAsync();

                Console.WriteLine($"{web.Id} - {web.Title}");
                Console.ReadKey();
            }
            #endregion
        }
    }
}

使用 SharePoint PnP 框架库在 CSOM 应用程序中使用此主体

在第一步中,添加 PnP 框架库 NuGet 包: https://www.nuget.org/packages/PnP.Framework

完成此作后,可以使用以下代码构造:

using PnP.Framework;
using System;

namespace AzureADCertAuth
{
    class Program
    {
        static void Main(string[] args)
        {
            var authManager = new AuthenticationManager("<application id>", "c:\\temp\\mycert.pfx", "<password>", "contoso.onmicrosoft.com");
            using (var cc = authManager.GetContext("https://contoso.sharepoint.com/sites/demo"))
            {
                cc.Load(cc.Web, p => p.Title);
                cc.ExecuteQuery();
                Console.WriteLine(cc.Web.Title);
            };
        }
    }
}

注意

PnP Framework 是一个开放源代码解决方案,其活动社区为其提供支持。 没有用于 Microsoft 开放源代码工具支持的 SLA。

在 PowerShell 脚本中使用此主体,但不依赖于 PnP PowerShell

通过 PFX 文件和密码

Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "<Full Path to the Microsoft.Identity.Client.dll file>"
Add-Type -Path "<Full Path to the Microsoft.IdentityModel.Abstractions.dll file>"

#Declare the Variables
$tenantId = "<Tenant_Id>"
$clientId = "<Client_Id>"
$certPath = "<Full Path to the PFX file>"
$certPassword = "<Password of the .pfx certificate>"
$siteUrl = "https://<Domain>.sharepoint.com/sites/<SiteName>"

# Load the certificate
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($certPath, $certPassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)

# Get the access token using MSAL
$authority = "https://login.microsoftonline.com/$tenantId"
$scope = New-Object System.Collections.Generic.List[string]
$scope.Add("https://<Domain>.sharepoint.com/.default")
$msalApp = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($clientId).WithAuthority($authority).WithCertificate($cert).Build()
$authResult = $msalApp.AcquireTokenForClient($scope).ExecuteAsync().Result
$accessToken = $authResult.AccessToken

$context = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)

# Attach event handler for Authorization header
$context.add_ExecutingWebRequest({
    param($sender, $e)
    $e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer $accessToken"
})

# Example: Load Web Title
$web = $context.Web
$context.Load($web)
$context.ExecuteQuery()
 
Write-Host "Site Title:" $web.Title -ForegroundColor Green

通过指纹从用户的证书存储区加载证书

Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "<Full Path to the Microsoft.Identity.Client.dll file>"
Add-Type -Path "<Full Path to the Microsoft.IdentityModel.Abstractions.dll file>"

$tenantId = "<Tenant_Id>"
$clientId = "<Client_Id>"
$thumbprint = "<Thumbprint value>"
$siteUrl = "https://<Domain>.sharepoint.com/sites/<SiteName>"

# Load the certificate from the CurrentUser\My store
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My", "CurrentUser")
$store.Open("ReadOnly")
$cert = $store.Certificates | Where-Object { $_.Thumbprint -eq $thumbprint }
$store.Close()

if (-not $cert) {
    Write-Error "Certificate with thumbprint $thumbprint not found."
    return
}

# Get the access token using MSAL
$authority = "https://login.microsoftonline.com/$tenantId"
$scope = New-Object System.Collections.Generic.List[string]
$scope.Add("https://<Domain>.sharepoint.com/.default")
$msalApp = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($clientId).WithAuthority($authority).WithCertificate($cert).Build()
$authResult = $msalApp.AcquireTokenForClient($scope).ExecuteAsync().Result
$accessToken = $authResult.AccessToken

# Output the token
$context = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)

# Attach event handler for Authorization header
$context.add_ExecutingWebRequest({
    param($sender, $e)
    $e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer $accessToken"
})

# Example: Load Web Title
$web = $context.Web
$context.Load($web)
$context.ExecuteQuery()

Write-Host "Site Title:" $web.Title -ForegroundColor Green

常见问题

除了使用证书以外,还能使用其他方法对我的 Azure AD 应用实现仅限应用访问吗?

不能,其他所有选项都已被 SharePoint Online 屏蔽,并会导致生成“访问遭拒”消息。