通过ASP.NET Core Blazor Web应用调用时,Web服务返回401(未授权)
我一直在按照 将ASP.NET Core Blazor Web应用与Microsoft Entra ID一起进行安全保护的指南 来操作,这看起来正是我需要的。
我的问题出现在在“MinimalApiJwt”中执行这行 app.MapGet("/weather-forecast", () =\> 时,出现了“HTTP 401 - Unauthorized”的错误。
移除 .RequireAuthorization() 显示该函数在没有这个检查的情况下可以运行,而将 (ClaimsPrincipal user) 作为DI值添加后,user.Identity.IsAuthenticated 是false。
我的起点是来自GitHub的这段代码 this code,与示例相比改动非常小,请注意我使用的是 "Microsoft Entra ID" 而不是 "Microsoft Entra External ID"。
我的修改 - 整体方案:
- 更新为使用
.slnx文件格式 - 更新NuGet包
项目 BlazorWebAppEntra (program.cs):
- 新增了行
msIdentityOptions.ClientSecret = SecretValue; - 创建了一些变量,通过新的
ReadConfig函数进行设置 - 根据Entra ID的注释替换占位符
项目 MinimalApiJwt (program.cs):
- 注释掉
.RequireAuthorization(),因为我遇到了HTTP 401错误 - 新增DI
(ClaimsPrincipal user),以便检查用户详细信息 - 创建了一些变量,通过新的
ReadConfig函数进行设置 - 根据Entra ID的注释替换占位符
来自 BlazorWebAppEntra (Program.cs) 的代码:
using System.Security.Claims;
using Azure.Core;
using Azure.Identity;
using BlazorWebAppEntra.Client.Weather;
using BlazorWebAppEntra.Components;
using BlazorWebAppEntra.Weather;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders.Distributed;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents()
.AddAuthenticationStateSerialization(options => options.SerializeAllClaims = true);
string ReadConfig(string key) => builder.Configuration[$"AzureAd:{key}"] ?? throw new Exception($"{key} not set");
string TenantID = ReadConfig("TenantId");
string ClientId = ReadConfig("ClientId");
string WebAPIAppClientID = ReadConfig("WebAPIAppClientID");
string WebAPIAppClientBaseURL = ReadConfig("WebAPIAppClientBaseURL");
string SecretValue = ReadConfig("SecretValue");
string Domain = ReadConfig("Domain");
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(msIdentityOptions =>
{
msIdentityOptions.CallbackPath = "/signin-oidc";
msIdentityOptions.ClientId = ClientId;
msIdentityOptions.Domain = Domain;
msIdentityOptions.Instance = "https://login.microsoftonline.com/";
msIdentityOptions.ResponseType = "code";
msIdentityOptions.TenantId = TenantID;
msIdentityOptions.ClientSecret = SecretValue;
})
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("DownstreamApi", configOptions =>
{
configOptions.BaseUrl = WebAPIAppClientBaseURL;
configOptions.Scopes = [ WebAPIAppClientID + "/Weather.Get" ];
})
.AddDistributedTokenCaches();
builder.Services.AddDistributedMemoryCache();
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(
options =>
{
options.Encrypt = true;
});
builder.Services.AddAuthorization();
builder.Services.AddScoped<IWeatherForecaster, ServerWeatherForecaster>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
app.UseWebAssemblyDebugging();
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
app.UseHttpsRedirection();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapGet("/weather-forecast", ([FromServices] IWeatherForecaster WeatherForecaster) =>
{
return WeatherForecaster.GetWeatherForecastAsync();
}).RequireAuthorization();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(BlazorWebAppEntra.Client._Imports).Assembly);
app.MapGroup("/authentication").MapLoginAndLogout();
app.Run();
来自 MinimalApiJwt (Program.cs) 的代码:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.Options;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
string ReadConfig(string key) => builder.Configuration[$"AzureAd:{key}"] ?? throw new Exception($"{key} not set");
string TenantID = ReadConfig("TenantId");
string WebAPIAppClientID = ReadConfig("WebAPIAppClientID");
builder.Services.AddAuthentication()
.AddJwtBearer("Bearer", jwtOptions =>
{
jwtOptions.Authority = "https://sts.windows.net/" + TenantID;
jwtOptions.Audience = "api://" + WebAPIAppClientID;
});
builder.Services.AddAuthorization();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument(); // Add NSwag services
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseOpenApi();
app.UseSwaggerUi();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weather-forecast", (ClaimsPrincipal user) =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
}); //.RequireAuthorization();
app.Run();
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Entra ID管理中心
我没有直接访问权限,因为这是由网络管理员设置的,但如果需要任何细节,请告诉我,我可以请他们提供。
先行致谢,感谢您的帮助
解决方案
首先,看来你的JWT授权机构设置不正确。目前,你使用的是:“https://sts.windows.net”,这对Entra ID v2无效。
你应该将代码改为:
jwtOptions.Authority = "https://login.microsoftonline.com/{tenantId}/v2.0";
请确保将 {tenantId} 替换为Entra的实际TenantId。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。