Blazor WASM调用ASP Web API时令牌无效及CORS重定向问题排查
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
;
// 默认对所有请求启用授权,包含两种方案以支持网页或API访问
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme
, OpenIdConnectDefaults.AuthenticationScheme
).RequireAuthenticatedUser().Build();
});
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI()
;
builder.Services.AddCors(x => x.AddPolicy("policy", new CorsPolicyBuilder().AllowAnyOrigin().AllowAnyHeader().Build()));
...
app.UseCors("policy");
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
// 开发环境启用Swagger
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
...
## WASM配置代码 ```csharp builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services.AddHttpClient<APIHttpClient>(x => x.BaseAddress = new Uri("https://localhost:5033")) .AddHttpMessageHandler<CorsAndAuthHttpHandler>(); builder.Services.AddTransient<CorsAndAuthHttpHandler>(); builder.Services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>( options => { options.ProviderOptions.DefaultAccessTokenScopes.Add("User.Read"); options.ProviderOptions.DefaultAccessTokenScopes.Add("Mail.Read"); }); builder.Services.AddScoped<IAuthenticationProvider, GraphAuthenticationProvider>(); builder.Services.AddScoped(sp => { return new GraphServiceClient( new HttpClient(), sp.GetRequiredService<IAuthenticationProvider>(), "https://graph.microsoft.com/v1.0/"); }); builder.Services.AddMsalAuthentication(x => { builder.Configuration.Bind("AzureAd", x.ProviderOptions.Authentication); }); builder.Services.AddAuthorizationCore(options => { options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); }); ... public class CorsAndAuthHttpHandler : AuthorizationMessageHandler { public CorsAndAuthHttpHandler(Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider provider, NavigationManager navigation) : base(provider, navigation) { ConfigureHandler(new string[] { "https://localhost:5033" }); } protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) { var resp = base.Send(request, cancellationToken); if (resp.StatusCode == System.Net.HttpStatusCode.Found) { } return resp; } }
错误信息
Access to fetch at 'https://login.microsoftonline.com/...' (redirected from 'https://localhost:5033/WeatherForecast') from origin 'https://localhost:5034' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
1. 修复认证方案注册冲突
连续两次调用AddAuthentication会覆盖默认方案配置,导致OIDC方案未被正确加载。改为一次注册多个认证方案:
builder.Services.AddAuthentication() .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")) // 注册OIDC方案 .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")); // 注册JWT方案
授权策略部分保持原代码不变即可。
2. 为WASM添加API专属权限范围
当前WASM请求的令牌只包含Graph的权限范围,没有Web API的专属范围,导致API判定令牌无效。需要添加API的范围:
builder.Services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>( options => { options.ProviderOptions.DefaultAccessTokenScopes.Add("User.Read"); options.ProviderOptions.DefaultAccessTokenScopes.Add("Mail.Read"); // 替换为你的Web API的Application ID URI+权限范围,格式如api://<API客户端ID>/access_as_user options.ProviderOptions.DefaultAccessTokenScopes.Add("api://你的API客户端ID/access_as_user"); });
同时要在Entra ID控制台中,确保WASM应用已被授予该API的权限。
3. 避免API触发OIDC重定向流程
OIDC的重定向逻辑是为浏览器请求设计的,API调用遇到无效令牌时不应触发重定向。可以通过以下方式解决:
- 在API的控制器/端点上显式指定使用JWT认证方案:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - 或者调整授权策略的方案顺序,优先使用JWT:
options.FallbackPolicy = new AuthorizationPolicyBuilder( JwtBearerDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme ).RequireAuthenticatedUser().Build();
解决令牌无效问题后,重定向不会发生,CORS错误自然消失(CORS错误是重定向到微软登录页导致的,不属于你的API的CORS控制范围)。
4. 清理冗余的Handler代码
CorsAndAuthHttpHandler中重写的Send方法没有实际作用,直接删除该重写方法即可,保持AuthorizationMessageHandler的默认逻辑,它会自动处理令牌附加和401时的令牌刷新。
内容的提问来源于stack exchange,提问作者Mdd M

