[英]Blazor WASM protected with Azure Active Directory can't call protected WebAPI
我目前正在开发一个 Blazor wasm webapp,它受 Azure Active Directory 和 WebAPI 保护,它也受 AAD 保护。 APP本身也调用MSGraph API获取用户基本信息。
当前工作:如果未经授权,用户将无法访问该页面,如果未经授权,他将被重定向到微软页面以登录/获得授权。 访问该页面后,我还可以按照本教程通过 msgraph 获取基本用户信息: 官方 MS GraphAPI 教程并添加 GraphClientExtension class。
我的网络API:
API 启动:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
builder.WithOrigins("*")
.AllowAnyMethod()
.AllowAnyHeader());
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "X.Y.API", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "X.Y.API v1"));
}
//app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
我要调用的控制器/端点:
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
AzureAD 设置
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "<company>.onmicrosoft.com",
"TenantId": "d3c98095-xyz",
"ClientId": "a51b0337-xyz",
}
Blazor Wasm:
程序.cs:
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
AddMsIdentityAuthentication(builder);
AddMsGraphServices(builder);
AddHttpClients(builder);
AddExternalServices(builder);
await builder.Build().RunAsync();
}
private static void AddHttpClients(WebAssemblyHostBuilder builder)
{
builder.Services.AddHttpClient<LocalHttpClient>(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
builder.Services.AddHttpClient<ProtectedAPIHttpClient>(client =>
{
client.BaseAddress = new Uri("http://localhost:5000");
});
}
private static void AddMsIdentityAuthentication(WebAssemblyHostBuilder builder)
{
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("a51b0337-xyz/Read");
options.ProviderOptions.LoginMode = "redirect";
});
}
private static void AddMsGraphServices(WebAssemblyHostBuilder builder)
{
builder.Services.AddGraphClient();
}
private static void AddExternalServices(WebAssemblyHostBuilder builder)
{
builder.Services.AddTelerikBlazor();
}
}
受保护的APIHttpClient
public class ProtectedAPIHttpClient
{
private readonly HttpClient _http;
public ProtectedAPIHttpClient(HttpClient http)
{
_http = http;
}
public async Task<string> GetDataTest()
{
try
{
var test = await _http.GetStringAsync("WeatherForecast");
Console.WriteLine(test);
return test;
}
catch (Exception e)
{
Console.WriteLine(e);
if(e is HttpRequestException ex)
{
Console.WriteLine((int)ex.StatusCode);
}
throw;
}
}
}
App.razor:
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
@if (!context.User.Identity.IsAuthenticated)
{
<RedirectToLogin/>
}
else
{
<p>You are not authorized to access this resource.</p>
}
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
重定向登录:
@inject NavigationManager _navigation
@code {
protected override void OnInitialized()
{
_navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(_navigation.Uri)}");
}
}
验证:
@page "/authentication/{action}"
@attribute [AllowAnonymous]
<RemoteAuthenticatorView Action="@Action">
<LoggingIn></LoggingIn>
<LogInFailed></LogInFailed>
<CompletingLoggingIn></CompletingLoggingIn>
<LogOut></LogOut>
<LogOutFailed></LogOutFailed>
<LogOutSucceeded></LogOutSucceeded>
<CompletingLogOut></CompletingLogOut>
</RemoteAuthenticatorView>
@code{
[Parameter]
public string Action { get; set; }
}
AzureAD 配置:
"AzureAd": {
"Authority": "https://login.microsoftonline.com/d3c98095-xyz",
"ClientId": "b02ce171-xyz",
"ValidateAuthority": true
},
我还向 _Imports.cs 添加了@attribute [Authorize]
几天来,我一直在纠结这个问题。 如果我删除/注释掉 program.cs 中的 API Scope "options.ProviderOptions.DefaultAccessTokenScopes" 一切正常:用户被迫登录并可以在应用程序中导航(并从中获取图片、名字等用户信息图表)。 但是,如果我添加 DefautAccessTokenScopes,则根本不可能登录页面。 它在某种登录重定向循环中。 我也尝试改用 AdditionalScopesToConsent,但结果相同。 我已经遵循了很多教程,并且在大多数教程中,像这样配置应用程序/api 对于一个简单的受保护 api 调用示例来说应该足够了……但我似乎误解了一些东西。 有谁知道或知道我做错了什么?
I checked my solution working similar to this, what I found is that App.razor should not contain NotAuthorized
section und AuthorizeRouteView
this should be in the MailLayout.razor or Index.razor etc. under AuthorizeView
.
App.razor 样品:
<CascadingAuthenticationState>
<CascadingBlazoredModal>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingBlazoredModal>
</CascadingAuthenticationState>
在某些 *.razor 页面上:
<AuthorizeView Policy="@($"{PermissionLevel.XYZ}")">
<Authorized>
<p>You are able to see the page</p>
<p> Even more content</p>
</Authorized>
</AuthorizeView>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.