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

在.NET Core 6/7/8中集成Microsoft Graph API 5.x.x的授权与调用问询

问题描述

我希望在.NET Core 6/7/8 Web API中使用Microsoft Graph API 5.x.x,同时支持委托权限与应用权限。需要提供带[Authorize]特性的控制器、Program.cs、appsettings.json示例,以及Microsoft Azure AD的应用注册流程;还要包含通过委托/应用令牌调用获取当前用户(Me)的示例。我多次尝试后在Program.cs的授权依赖注入(DI)集成环节受阻,以下是我使用的NuGet包、控制器、Program.cs及appsettings.json的完整代码:


现有代码

我使用的NuGet包

<ItemGroup>
  <PackageReference Include="Microsoft.Graph" Version="5.41.0" />
  <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
  <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10" NoWarn="NU1605" />
  <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.10" NoWarn="NU1605" />
  <PackageReference Include="Microsoft.Identity.Client" Version="4.55.0" />
  <PackageReference Include="Microsoft.Identity.Web" Version="2.13.3" />
  <PackageReference Include="Microsoft.Identity.Web.GraphServiceClient" Version="2.13.3" />
  <PackageReference Include="Microsoft.Identity.Web.UI" Version="2.13.3" />
</ItemGroup>

我的控制器代码

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using Microsoft.Kiota.Abstractions;
using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using Microsoft.Graph;

namespace WebApp.Controllers
{
    [Route("api/v1.0/user")]
    [ApiController]
    [Authorize]
    public class MasterController : Controller
    {
        private readonly ILogger<MasterController> _logger;
        private readonly GraphServiceClient _graphServiceClient;
        public MasterController(GraphServiceClient graphServiceClient, ILogger<MasterController> logger)
        {
            _logger = logger;
            _graphServiceClient = graphServiceClient;
        }

        [HttpGet("GetMyProfile")]
        public async Task<IActionResult> GetMyProfile()
        {
            try
            {
                var user = await _graphServiceClient.Me.GetAsync();
                return this.Ok(user);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error getting user profile");
                return this.Problem(ex.Message);
            }
            
        }
    }
}

我的Program.cs代码

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.Kiota.Abstractions.Authentication;


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
     .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
         .EnableTokenAcquisitionToCallDownstreamApi()
             .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
             .AddInMemoryTokenCaches();
builder.Services.AddAuthorization();

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
var scopeRequiredByApi = app.Configuration["AzureAd:Scopes"] ?? "";

我的appsettings.json配置

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "XXXXX.onmicrosoft.com",
    "TenantId": "XXXXXXXXXXXXXXXXXXXX",
    "ClientId": "XXXXXXXXXXXXXXXXXXXX",
    "CallbackPath": "/signin-oidc",
    "Scopes": "access_as_user",
    "ClientSecret": "XXXXXXXXXXXXXXXX",
    "ClientCertificates": []
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com/v1.0",
    "Scopes": "user.read"
  }
}

解决方案:支持委托与应用权限的集成配置

一、修正后的Program.cs配置

核心调整:移除Web UI相关依赖(Web API无需UI组件),分别注册委托/应用权限的GraphServiceClient实例,配置区分两种权限的授权策略。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.Resource;

var builder = WebApplication.CreateBuilder(args);

// 1. 配置身份验证,同时支持委托和应用权限的Graph调用
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    // 注册委托权限用的Graph客户端(基于用户令牌)
    .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph:Delegated"))
    // 注册应用权限用的Graph客户端(基于应用令牌)
    .AddMicrosoftGraphAppOnly(builder.Configuration.GetSection("MicrosoftGraph:Application"))
    .AddInMemoryTokenCaches();

// 2. 配置授权策略,区分委托和应用权限访问
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("DelegatedAccess", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("scp", builder.Configuration["AzureAd:DelegatedScopes"]?.Split(',') ?? Array.Empty<string>());
    });

    options.AddPolicy("ApplicationAccess", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("roles", builder.Configuration["AzureAd:ApplicationRoles"]?.Split(',') ?? Array.Empty<string>());
    });
});

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

二、更新后的appsettings.json配置

新增区分委托/应用权限的Graph配置,明确对应权限范围:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "XXXXX.onmicrosoft.com",
    "TenantId": "XXXXXXXXXXXXXXXXXXXX",
    "ClientId": "XXXXXXXXXXXXXXXXXXXX",
    "ClientSecret": "XXXXXXXXXXXXXXXX",
    "DelegatedScopes": "user.read",
    "ApplicationRoles": "User.Read.All"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "MicrosoftGraph": {
    "Delegated": {
      "BaseUrl": "https://graph.microsoft.com/v1.0",
      "Scopes": "user.read"
    },
    "Application": {
      "BaseUrl": "https://graph.microsoft.com/v1.0",
      "Scopes": "https://graph.microsoft.com/.default"
    }
  }
}

三、支持两种权限的控制器示例

通过构造函数注入两种GraphServiceClient实例,用授权策略限制接口访问范围:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
using Microsoft.Identity.Web;

namespace WebApp.Controllers
{
    [Route("api/v1.0/users")]
    [ApiController]
    public class UserController : ControllerBase
    {
        private readonly ILogger<UserController> _logger;
        private readonly GraphServiceClient _delegatedGraphClient;
        private readonly GraphServiceClient _applicationGraphClient;

        // 注入委托权限和应用权限的Graph客户端
        public UserController(
            ILogger<UserController> logger,
            GraphServiceClient delegatedGraphClient,
            [FromKeyedServices("MicrosoftGraphAppOnly")] GraphServiceClient applicationGraphClient)
        {
            _logger = logger;
            _delegatedGraphClient = delegatedGraphClient;
            _applicationGraphClient = applicationGraphClient;
        }

        /// <summary>
        /// 委托权限调用:获取当前用户信息(仅用户上下文可用Me接口)
        /// </summary>
        [HttpGet("me")]
        [Authorize(Policy = "DelegatedAccess")]
        public async Task<IActionResult> GetCurrentUser()
        {
            try
            {
                var user = await _delegatedGraphClient.Me.GetAsync();
                return Ok(user);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取当前用户信息失败");
                return Problem(ex.Message);
            }
        }

        /// <summary>
        /// 应用权限调用:获取指定用户信息(应用权限无法使用Me接口)
        /// </summary>
        [HttpGet("{userId}")]
        [Authorize(Policy = "ApplicationAccess")]
        public async Task<IActionResult> GetUserById(string userId)
        {
            try
            {
                var user = await _applicationGraphClient.Users[userId].GetAsync();
                return Ok(user);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取用户信息失败");
                return Problem(ex.Message);
            }
        }
    }
}

四、Azure AD应用注册流程

1. 创建应用注册

  • 登录Azure门户,进入Azure Active Directory → 应用注册 → 新注册
  • 填写应用名称,选择账户类型(如“仅此组织目录中的账户”),重定向URI留空,点击注册

2. 配置API权限

委托权限(用户上下文调用)

  • 进入API权限 → 添加权限 → Microsoft Graph → 委托权限
  • 搜索并添加User.Read权限,点击添加权限
  • 点击授予管理员同意(需租户管理员权限)

应用权限(后台服务调用)

  • 再次点击添加权限 → Microsoft Graph → 应用权限
  • 搜索并添加User.Read.All权限,点击添加权限
  • 点击授予管理员同意(需租户管理员权限)

3. 生成客户端密钥

  • 进入证书和密码 → 客户端密码 → 新客户端密码
  • 填写描述和过期时间,点击添加,保存生成的密码值(仅显示一次)

4. 自定义范围(可选)

  • 进入公开API → 添加范围,设置范围名称(如access_as_user),保存后记录完整范围URL

五、调用示例

委托权限调用(用户令牌)

使用OAuth2授权码流获取用户令牌,请求头携带:

Authorization: Bearer {用户访问令牌}

令牌需包含User.Read范围。

应用权限调用(应用令牌)

使用客户端凭证流获取应用令牌,请求头携带:

Authorization: Bearer {应用访问令牌}

令牌需包含User.Read.All角色。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 20:10:54