繁体   English   中英

Blazor WASM 受 Azure 保护 Active Directory 无法调用受保护的 WebAPI

[英]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 Azure 配置 1 API Azure 配置 2

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",
  }

我的应用程序: Azure 应用设置 1 Azure 应用程序设置 2 Azure APP 见闻 3

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM