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

Azure Web应用调用Azure Function Web API的正确SSO OAuth流程

解决方案:使用OAuth 2.0 On-Behalf-Of(OBO)流程实现用户身份传递

你需要用OAuth 2.0的On-Behalf-Of(OBO)流程,这是专门解决「中间服务代表已登录用户调用下游受保护API」的场景,完全匹配你的需求,同时支持单租户和多租户模式切换。

为什么选OBO流程?

你的Web应用已经通过Azure AD完成用户登录,现在要以该用户身份调用同样受Azure AD保护的Function API——OBO流程允许Web应用拿着用户的身份凭证,向Azure AD申请访问Function的专属Token,实现用户上下文的传递,让API返回对应用户的专属数据。


实现步骤(兼顾单/多租户)

一、Azure AD应用注册配置

1. Web应用的应用注册

  • 单租户模式:在「API权限」里添加Function应用注册的委托权限(必须是委托权限,因为是用户上下文访问),然后完成管理员同意。同时在「证书和密码」里创建客户端密码(或证书),用于Web应用向Azure AD请求OBO Token时做身份验证。
  • 多租户模式:除了添加委托权限,还要把Web应用的「支持的账户类型」改成「任何组织目录中的账户」;权限同意方式可选择用户同意或管理员同意(根据你的业务需求)。

2. Function的应用注册

  • 单租户模式:在「公开API」里定义一个自定义范围(比如api://<Function-App-Client-ID>/user_access),设置为「管理员和用户均可同意」。
  • 多租户模式:同样定义自定义范围,确保「支持的账户类型」对应多租户,并且范围的受众允许其他租户的应用访问。

二、Web应用端实现(获取OBO Token)

假设你的Web应用是ASP.NET Core,示例代码如下:

1. 配置Azure AD认证(Program.cs)

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

2. 在控制器中获取Token并调用Function

private readonly ITokenAcquisition _tokenAcquisition;

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

public async Task<IActionResult> CallFunction()
{
    // 替换为你Function应用注册的自定义范围
    string[] scopes = new[] { "api://<Function-Client-ID>/user_access" };
    // 获取代表用户访问Function的Token
    string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes);

    // 调用Azure Function
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    var response = await client.GetAsync("https://your-function-app.azurewebsites.net/api/YourFunction");
    
    // 处理响应逻辑...
    return View();
}

3. 配置文件(appsettings.json)

  • 单租户示例:
"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "你的租户域名.com",
  "TenantId": "你的租户ID",
  "ClientId": "Web应用的ClientID",
  "ClientSecret": "Web应用的客户端密码",
  "CallbackPath": "/signin-oidc"
}
  • 多租户示例(修改TenantId为common):
"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "TenantId": "common",
  "ClientId": "Web应用的ClientID",
  "ClientSecret": "Web应用的客户端密码",
  "CallbackPath": "/signin-oidc"
}

三、Azure Function(.NET 6 Isolated模式)的认证配置

因为是隔离模式,无法用门户向导,需要手动配置:

1. 配置Azure AD认证(Program.cs)

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddMicrosoftIdentityWebApi(options =>
            {
                // 单租户配置
                options.TokenValidationParameters.ValidAudience = "api://<Function-Client-ID>";
                options.TokenValidationParameters.ValidIssuer = "https://login.microsoftonline.com/<你的租户ID>/v2.0";
                
                // 多租户配置(替换上面的Issuer,取消注释下面代码)
                // options.TokenValidationParameters.ValidIssuer = "https://login.microsoftonline.com/common/v2.0";
                // options.TokenValidationParameters.IssuerValidator = (issuer, token, parameters) =>
                // {
                //     // 验证Issuer来自合法Azure AD租户
                //     if (issuer.StartsWith("https://login.microsoftonline.com/"))
                //         return issuer;
                //     throw new SecurityTokenInvalidIssuerException("非法Issuer");
                // };
            }, options =>
            {
                options.Instance = "https://login.microsoftonline.com/";
                options.ClientId = "<Function-Client-ID>";
                options.TenantId = "<你的租户ID>"; // 多租户改为"common"
            });
    })
    .Build();

host.Run();

2. 启用认证并获取用户信息

在Function类上添加[Authorize]属性,确保只有携带有效Token的请求能访问,同时可以从请求上下文获取用户信息:

[Function("YourFunction")]
[Authorize]
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req)
{
    // 获取当前用户的身份信息
    var user = req.HttpContext.User;
    string userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    string userName = user.FindFirst(ClaimTypes.Name)?.Value;

    // 返回用户专属信息
    var response = req.CreateResponse(HttpStatusCode.OK);
    await response.WriteAsJsonAsync(new { 
        用户ID = userId, 
        用户名 = userName, 
        专属内容 = "根据用户身份返回的个性化数据" 
    });
    return response;
}

3. 本地调试配置(local.settings.json)

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "AzureAd:Instance": "https://login.microsoftonline.com/",
    "AzureAd:ClientId": "<Function-Client-ID>",
    "AzureAd:TenantId": "<你的租户ID>" // 多租户改为"common"
  }
}

四、关键注意事项

  • 权限范围:Web应用申请的必须是Function定义的委托权限,且完成同意(单租户需管理员同意,多租户可用户/管理员同意)。
  • Token缓存:Web应用用AddInMemoryTokenCaches做本地缓存(生产环境建议用分布式缓存),避免重复向Azure AD请求Token。
  • 多租户Issuer验证:多租户模式下必须自定义Issuer验证逻辑,防止非法Token访问。
  • Function授权级别:HttpTrigger的AuthorizationLevel要设为Anonymous,因为我们用Azure AD认证保护,而非Function内置的密钥认证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 03:05:58