无法从受Azure AD保护的.NET Web API项目调用SharePoint Rest API
问题根因
你从请求头拿到的令牌是前端申请的、受众为你自有Web API的访问令牌,其aud声明为你Web API的Client ID,而SharePoint要求访问令牌的受众必须为SharePoint服务,所以直接调用必然返回401,确实需要单独申请面向SharePoint的访问令牌。
推荐方案
采用Azure AD的代理流(On-Behalf-Of, OBO),这是中间层Web API接收到用户令牌后,代表用户访问下游API的标准实现方案,不需要额外注册新的Azure AD应用,直接复用你现有Web API的注册信息即可。
具体操作步骤
1. 调整现有Azure AD应用配置
- 进入你已注册的Azure AD应用管理页,打开「API权限」页,添加SharePoint Online的委托权限,根据你的业务需求选择对应权限(比如读取站点内容选
Sites.Read.All,读写选Sites.ReadWrite.All),添加后点击「授予管理员同意」完成权限激活。 - 进入「证书和密码」页,新建一个客户端密码,保存好密码值,后续配置需要用到。
2. 调整项目配置与依赖
- 确保已安装NuGet包:
Microsoft.Identity.Web、Microsoft.Identity.Web.TokenCache,注意.NET Core 3.1环境下请选择1.x版本的包,高版本不再兼容.NET Core 3.1。 - 修改
Startup.cs的身份验证配置,启用令牌获取能力和内存缓存:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(Configuration) // 启用下游API令牌获取能力 .EnableTokenAcquisitionToCallDownstreamApi() // 添加内存令牌缓存,生产环境可替换为分布式缓存 .AddInMemoryTokenCaches();
- 修改
appsettings.json的AzureAd节点,补充刚才生成的客户端密码:
"AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "mydomain.onmicrosoft.com", "ClientId": "你的Web API Client ID", "TenantId": "你的租户ID", "Audience": "你的Web API Client ID", "ClientSecret": "刚才生成的客户端密码", "AllowWebApiToBeAuthorizedByACL": true }
3. 控制器内调用SharePoint REST API
在控制器中注入ITokenAcquisition服务,先申请面向SharePoint的访问令牌,再用新令牌发起请求即可,示例代码如下:
using Microsoft.Identity.Web; using System.Net.Http.Headers; [Route("api/v1/[controller]")] [ApiController] [Authorize] public class SpController : ControllerBase { private readonly ITokenAcquisition _tokenAcquisition; private readonly IHttpClientFactory _httpClientFactory; public SpController(ITokenAcquisition tokenAcquisition, IHttpClientFactory httpClientFactory) { _tokenAcquisition = tokenAcquisition; _httpClientFactory = httpClientFactory; } [HttpGet("get-site-info")] public async Task<IActionResult> GetSiteInfo() { // 替换为你的SharePoint站点域名和对应权限范围 var spoScopes = new[] { "https://你的租户名.sharepoint.com/Sites.ReadWrite.All" }; // 申请面向SharePoint的访问令牌 var spoAccessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(spoScopes); var httpClient = _httpClientFactory.CreateClient(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", spoAccessToken); // 替换为你实际要调用的SharePoint REST API地址 var response = await httpClient.GetAsync("https://你的租户名.sharepoint.com/sites/你的站点名/_api/web"); if (response.IsSuccessStatusCode) { var result = await response.Content.ReadAsStringAsync(); return Ok(result); } return StatusCode((int)response.StatusCode, await response.Content.ReadAsStringAsync()); } }
注意事项
- 前端申请你的Web API访问令牌时,scope必须填写你Web API暴露的权限标识,不能直接用SharePoint的scope。
- 若调用时报权限不足错误,检查Azure AD应用的SharePoint权限是否已经完成管理员同意,权限范围是否匹配你要执行的操作。
- 生产环境建议替换内存令牌缓存为分布式缓存(比如Redis),避免多实例部署时重复申请令牌。
内容的提问来源于stack exchange,提问作者Neelam
相关产品推荐
相关产品推荐

