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

Azure AD场景下C# Web API如何基于Angular SPA令牌代用户访问Graph API

解决方案

你的场景适用Azure AD的OAuth 2.0 代表流(On-Behalf-Of, OBO),核心逻辑是:中间层API(你的MyAPI)拿到SPA传入的、受众为自身的用户令牌后,用这个令牌向Azure AD换发受众为Microsoft Graph的委托访问令牌,再用新令牌调用Graph接口。

1 配置Azure AD应用注册

1.1 配置MyAPI的应用注册

  • 进入MyAPI的应用注册页,打开「API权限」菜单:
    • 添加Microsoft Graph的委托权限,比如你需要的User.Read,不要选应用权限
    • 权限添加完成后,点击「授予管理员同意」(仅组织内部使用场景需要,多租户场景由租户管理员自行同意)
  • 打开「证书和密码」菜单:
    • 新增一个客户端密码,保存好生成的密码值,后续代码配置会用到
  • (可选)避免用户二次授权:打开「公开API」菜单,在access_as_user作用域的「已知客户端应用」配置项中,添加MySPA的客户端ID

1.2 无需修改MySPA的应用注册

SPA端请求MyAPI的逻辑保持不变,还是请求scope为api://<MyAPI客户端ID>/access_as_user的访问令牌即可。

2 改造ASP.NET Core API代码

推荐使用微软官方的Microsoft.Identity.Web库处理代表流逻辑,无需手动解析令牌、处理授权请求。

2.1 安装依赖NuGet包

Install-Package Microsoft.Identity.Web
Install-Package Microsoft.Identity.Web.MicrosoftGraph

2.2 修改服务注册配置

在Program.cs中添加身份验证和代表流支持:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// 加这一段身份验证配置
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi() // 启用代表流能力
    .AddMicrosoftGraph(builder.Configuration.GetSection("Graph"))
    .AddInMemoryTokenCaches(); // 生产环境建议替换为分布式缓存(如Redis)

// 原有其他服务注册逻辑保留
builder.Services.AddControllers();
// ... 其他配置

var app = builder.Build();

// 确保启用身份验证、授权中间件
app.UseAuthentication();
app.UseAuthorization();

// ... 原有中间件、路由配置

2.3 新增配置项

在appsettings.json中添加Azure AD和Graph的配置:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "你的Azure AD租户ID",
    "ClientId": "MyAPI的客户端ID",
    "ClientSecret": "你之前生成的MyAPI客户端密码"
  },
  "Graph": {
    "BaseUrl": "https://graph.microsoft.com/v1.0",
    "Scopes": "User.Read" // 多个Graph权限用空格分隔
  }
}

2.4 改写接口逻辑

直接注入GraphServiceClient即可调用Graph接口,无需手动处理令牌:

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class ProfileController : ControllerBase
{
    private readonly GraphServiceClient _graphClient;

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

    [HttpGet]
    public async Task<IActionResult> GetProfile()
    {
        var userProfile = await _graphClient.Me.GetAsync();
        return Ok(userProfile);
    }
}

如果你不想用Graph SDK,也可以注入ITokenAcquisition手动获取Graph访问令牌:

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class ProfileController : ControllerBase
{
    private readonly ITokenAcquisition _tokenAcquisition;

    public ProfileController(ITokenAcquisition tokenAcquisition)
    {
        _tokenAcquisition = tokenAcquisition;
    }

    [HttpGet]
    public async Task<IActionResult> GetProfile()
    {
        // 主动用代表流获取Graph的访问令牌
        string graphAccessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { "User.Read" });
        
        // 用新令牌调用Graph接口
        var result = await "https://graph.microsoft.com/v1.0/me/"
            .WithOAuthBearerToken(graphAccessToken)
            .WithHeader("Content-Type", "application/json")
            .GetJsonAsync();
        return Ok(result);
    }
}

3 注意事项

  • 不要用应用权限调用Graph,应用权限拿到的令牌没有用户上下文,调用/me接口会报错,不符合委托访问的需求
  • 生产环境不要使用内存缓存存储令牌,建议替换为Redis等分布式缓存,避免多实例部署时重复请求令牌
  • 如果需要调用多个下游API,可以在EnableTokenAcquisitionToCallDownstreamApi后配置多个下游服务的权限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 05:09:02