You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

混合AD场景下Web API通过MS Graph获取用户日历的认证问题

混合AD场景下仅更新Web API调用MS Graph获取日历事件的最简方案

场景回顾

  • 混合Active Directory环境:本地AD与Azure AD同步,正迁移本地Exchange邮箱至Exchange Online
  • 现有系统:
    • 内部部署的JS SPA通过带凭据的Fetch API调用.NET Core Web API获取日历事件
    • Web API仅允许特定AD组用户访问,当前以应用池身份通过EWS连接本地Exchange读取日历
    • 核心需求:Web API改为代表用户调用MS Graph读取日历,且仅修改Web API代码,不改动SPA
  • 已遇问题:尝试AcquireTokenByIntegratedWindowsAuth时触发错误'Integrated Windows Auth is not supported for managed users',原因是无本地ADFS,同步到Azure AD的托管用户不支持IWA

仅修改Web API的解决方案

方案1:Kerberos约束委派 + On-Behalf-Of(OBO)流(推荐,严格代表用户身份)

此方案基于Web API已有的Windows身份验证,通过OBO流将用户身份传递给MS Graph,完全无需修改SPA。

步骤1:配置Azure AD应用注册

  • 注册Web API类型的应用,记录客户端ID和租户ID
  • 为应用添加委派权限:Microsoft Graph > Calendars.Read,并完成管理员同意
  • 生成客户端密钥(或使用证书),用于Web API向Azure AD验证身份

步骤2:确保Web API启用Windows身份验证

在.NET Core Web API的Program.cs中启用Windows身份验证:

builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
    .AddNegotiate();

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = options.DefaultPolicy;
});

验证Web API能正确获取当前访问用户的UPN(User.Identity.Name应为同步到Azure AD的用户UPN)

步骤3:实现OBO流获取Graph令牌并调用API

在Web API中集成MSAL.NET,通过OBO流获取用户的Graph访问令牌:

using Microsoft.Identity.Client;
using Microsoft.Graph;

// 注册MSAL客户端
builder.Services.AddScoped<IConfidentialClientApplication>(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    return ConfidentialClientApplicationBuilder
        .Create(config["AzureAd:ClientId"])
        .WithTenantId(config["AzureAd:TenantId"])
        .WithClientSecret(config["AzureAd:ClientSecret"])
        .Build();
});

// 注册Graph服务客户端
builder.Services.AddScoped<GraphServiceClient>(async sp =>
{
    var msalClient = sp.GetRequiredService<IConfidentialClientApplication>();
    var httpContext = sp.GetRequiredService<IHttpContextAccessor>().HttpContext;
    var userObjectId = httpContext.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;

    // 获取用户的Graph访问令牌
    var oboResult = await msalClient.AcquireTokenOnBehalfOf(
        new[] { "https://graph.microsoft.com/Calendars.Read" },
        new UserAssertion(userObjectId)
    ).ExecuteAsync();

    return new GraphServiceClient(new DelegateAuthenticationProvider(request =>
    {
        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", oboResult.AccessToken);
        return Task.CompletedTask;
    }));
});

之后在API接口中直接注入GraphServiceClient调用日历接口即可:

[ApiController]
[Route("[controller]")]
public class CalendarController : ControllerBase
{
    private readonly GraphServiceClient _graphClient;

    public CalendarController(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }

    [HttpGet]
    public async Task<IActionResult> GetCalendarEvents()
    {
        var events = await _graphClient.Me.Events.Request().GetAsync();
        return Ok(events);
    }
}

步骤4:配置本地AD的Kerberos约束委派

在本地AD中为Web API应用池的运行账户配置约束委派,允许其委派到Azure AD应用的服务主体:

  1. 打开应用池账户的AD属性,切换到「委派」标签
  2. 选择「信任此用户委派指定的服务」,点击「添加」
  3. 查找并添加你在Azure AD注册的应用的服务主体(可通过客户端ID定位)
  4. 保存配置后重启Web服务器

方案2:应用权限 + 用户筛选(简化方案,权限范围更大)

如果业务允许Web API以应用身份读取所有用户日历,再根据当前登录用户筛选,此方案无需配置Kerberos,实现更简单:

步骤1:配置Azure AD应用注册

  • 注册应用后添加应用权限:Microsoft Graph > Calendars.Read.All,完成管理员同意
  • 生成客户端密钥(或证书)

步骤2:Web API使用客户端凭据流调用Graph

// 注册MSAL和Graph客户端
builder.Services.AddScoped<IConfidentialClientApplication>(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    return ConfidentialClientApplicationBuilder
        .Create(config["AzureAd:ClientId"])
        .WithTenantId(config["AzureAd:TenantId"])
        .WithClientSecret(config["AzureAd:ClientSecret"])
        .Build();
});

builder.Services.AddScoped<GraphServiceClient>(async sp =>
{
    var msalClient = sp.GetRequiredService<IConfidentialClientApplication>();
    var clientCredResult = await msalClient.AcquireTokenForClient(
        new[] { "https://graph.microsoft.com/.default" }
    ).ExecuteAsync();

    return new GraphServiceClient(new DelegateAuthenticationProvider(request =>
    {
        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", clientCredResult.AccessToken);
        return Task.CompletedTask;
    }));
});

在接口中根据当前用户UPN筛选日历:

[HttpGet]
public async Task<IActionResult> GetCalendarEvents()
{
    var userUpn = User.Identity.Name;
    var events = await _graphClient.Users[userUpn].Events.Request().GetAsync();
    return Ok(events);
}

注意:此方案需要Web API拥有读取所有用户日历的权限,适合内部可信系统,需评估安全风险。


内容的提问来源于stack exchange,提问作者datahandler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 12:15:44