簡體   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