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
相关产品推荐
相关产品推荐

