.NET Web应用中Azure OIDC与Bearer Token共存认证异常排查
问题:.NET Web应用同时支持Azure OAuth和Bearer Token认证失败
在.NET Web应用中借助Azure实现OAuth和Bearer Token两种请求认证方式时,OAuth认证功能正常——访问带有[Authorize]特性的控制器路由时,会触发登录提示且认证可正常通过,但Bearer Token认证无法生效,需要让所有控制器同时支持这两种认证方式。
获取Bearer Token的PowerShell方法
Install-Module -Name MSAL.PS $Token = Get-MsalToken -ClientId 'clientId' -TenantId 'tenantId' $Token.AccessToken
当前Program.cs代码
public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddJwtBearer(opt => { var conf = new MicrosoftIdentityOptions(); builder.Configuration.GetSection(Constants.AzureAd).Bind(conf); opt.Audience = $"api://{conf.ClientId}"; opt.Authority = $"{conf.Instance}{conf.TenantId}"; }) .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")); builder.Services.AddControllers(); builder.Services.AddHttpForwarder(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run(); }
当前appsettings.json配置
{ "AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "qualified.domain.name", "TenantId": "", "ClientId": "", "CallbackPath": "/signin-oidc" } }
解决方案
问题核心是默认认证方案设置为OpenIdConnectDefaults.AuthenticationScheme,且未配置授权策略同时允许两种认证方式,导致Bearer Token认证流程未被触发。需按以下步骤修改:
1. 调整认证服务配置
将默认认证方案改为CookieAuthenticationDefaults.AuthenticationScheme(OAuth流程最终通过Cookie保存身份),同时为JWT Bearer指定明确的方案名称,方便后续授权策略引用:
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddJwtBearer("Bearer", opt => { var conf = new MicrosoftIdentityOptions(); builder.Configuration.GetSection("AzureAd").Bind(conf); opt.Audience = $"api://{conf.ClientId}"; opt.Authority = $"{conf.Instance}{conf.TenantId}/v2.0"; // 使用Azure AD v2端点 // 配置Token验证参数,确保签名、受众、签发者有效性 opt.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = $"{conf.Instance}{conf.TenantId}/v2.0", ValidateAudience = true, ValidAudience = $"api://{conf.ClientId}", ValidateLifetime = true }; }) .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
2. 配置全局授权策略
添加全局授权策略,让所有带[Authorize]特性的控制器同时接受Cookie(OAuth流程)和Bearer Token两种认证方式:
builder.Services.AddAuthorization(options => { var defaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme, "Bearer") .RequireAuthenticatedUser() .Build(); options.DefaultPolicy = defaultPolicy; });
3. 验证Token有效性
通过JWT解码工具(如本地离线解码)确认获取的Token符合要求:
aud字段需匹配api://{clientId}iss字段需为https://login.microsoftonline.com/{tenantId}/v2.0
完整修改后的Program.cs
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Identity.Web; using Microsoft.IdentityModel.Tokens; public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // 配置双认证方案 builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddJwtBearer("Bearer", opt => { var conf = new MicrosoftIdentityOptions(); builder.Configuration.GetSection("AzureAd").Bind(conf); opt.Audience = $"api://{conf.ClientId}"; opt.Authority = $"{conf.Instance}{conf.TenantId}/v2.0"; opt.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = $"{conf.Instance}{conf.TenantId}/v2.0", ValidateAudience = true, ValidAudience = $"api://{conf.ClientId}", ValidateLifetime = true }; }) .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")); // 配置全局授权策略 builder.Services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme, "Bearer") .RequireAuthenticatedUser() .Build(); }); builder.Services.AddControllers(); builder.Services.AddHttpForwarder(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run(); }
内容的提问来源于stack exchange,提问作者Node.JS
相关产品推荐
相关产品推荐

