Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa guida illustra come proteggere un'applicazione distribuita .NET Aspire con Microsoft Entra ID l'autenticazione e l'autorizzazione. Si tratta di:
-
Front-end di Blazor Server (
MyService.Web): accesso utente con OpenID Connect e acquisizione di token -
Back-end protetto dell'API (
MyService.ApiService): validazione JWT tramite Microsoft.Identity.Web - Flusso end-to-end: Blazor acquisisce i token di accesso e chiama l'API protetta con l'individuazione del servizio Aspire
In questa guida si presuppone che sia stato avviato un progetto Aspire creato usando il comando seguente:
aspire new aspire-starter --name MyService
Prerequisiti
- .NET 9 SDK o versione successiva
- CLI .NET Aspire - Vedere Install Aspire CLI
- Microsoft Entra tenant : vedere Register apps in Microsoft Entra ID per la configurazione
Suggerimento
Nuovo su Aspire? Consulta la panoramica di .NET Aspire.
Comprendere il flusso di lavoro in due fasi
Questa guida segue un approccio in due fasi:
| Fase | Che succede | Result |
|---|---|---|
| Fase 1 | Aggiungere il codice di autenticazione con valori segnaposto | L'app si compila ma non viene eseguita |
| Fase 2 | Effettuare il provisioning delle registrazioni delle applicazioni Microsoft Entra | L'app viene eseguita con autenticazione reale |
Registrare le app in Microsoft Entra ID
Prima che l'app possa autenticare gli utenti, sono necessarie due registrazioni dell'app in Microsoft Entra:
| Registrazione dell'app | Scopo | Configurazione della chiave |
|---|---|---|
API (MyService.ApiService) |
Convalida i token di ingresso | URI ID applicazione, access_as_user ambito |
App Web (MyService.Web) |
Autentica gli utenti, acquisisce i token | URI di reindirizzamento, segreto client, autorizzazioni API |
Se sono già state configurate registrazioni per le app, sono necessari questi valori per appsettings.json:
- TenantId - ID tenant di Microsoft Entra
- API ClientId - ID applicazione (client) della registrazione dell'app per le API
-
URI ID app per le API : in genere
api://<api-client-id>(usato inAudienceseScopes) - ClientId app Web - ID applicazione (client) della registrazione dell'app Web
- Segreto client (o certificato): credenziali per l'app Web (store nei segreti utente, non appsettings.json)
-
Ambiti — Gli ambiti che l'applicazione web richiede, ad esempio
api://<api-client-id>/.defaultoapi://<api-client-id>/access_as_user
Passaggio 1: Registrare l'API
- Vai a Interfaccia di amministrazione di Microsoft Entra>Identità>Applicazioni>Registrazioni delle app.
- Seleziona Nuova registrazione.
-
Nome:
MyService.ApiService - Tipi di account supportati: Account solo in questa directory organizzativa (tenant singolo)
- Selezionare Registrazione.
-
Nome:
- Passare a Esporre un'API>Aggiungi accanto all'URI ID applicazione.
- Accettare il valore predefinito (
api://<client-id>) o personalizzarlo. - Selezionare Aggiungi un ambito:
-
Nome ambito:
access_as_user - Chi può fornire il consenso: Amministratori e utenti
- Nome visualizzato del consenso amministratore: Accedere all'API MyService
- Descrizione del consenso amministratore: Consente all'app di accedere all'API MyService per conto dell'utente connesso.
- Seleziona Aggiungi ambito.
-
Nome ambito:
- Accettare il valore predefinito (
- Copiare l'ID applicazione (client), necessario per entrambi i
appsettings.jsonfile.
Per altre informazioni, vedere Avvio rapido: Configurare un'app per esporre un'API Web.
Passaggio 2: Registrare l'app Web
- Passare a Registrazioni app>Nuova registrazione.
-
Nome:
MyService.Web - Tipi di account supportati: Solo gli account in questa directory organizzativa
-
URI di reindirizzamento: Selezionare Web e immettere l'URL dell'app +
/signin-oidc- Per lo sviluppo locale:
https://localhost:7001/signin-oidc(controlla il tuolaunchSettings.jsonper la porta effettiva)
- Per lo sviluppo locale:
- Selezionare Registrazione.
-
Nome:
- Passare a Authentication e > per aggiungere tutti gli URL di sviluppo (da ).
- Passare a Certificati e segreti>Segreti client>Nuovo segreto client.
- Aggiungere una descrizione e una scadenza.
- Copiare immediatamente il valore del segreto. Non verrà più visualizzato.
- Passare a Autorizzazioni> APIAggiungere un'autorizzazione>Api personali.
- Seleziona
MyService.ApiService. - Selezionare
access_as_user>Aggiungi autorizzazioni. - Selezionare Concedi consenso amministratore per [tenant] (o agli utenti viene richiesto al primo utilizzo).
- Seleziona
- Copiare l'ID applicazione (client) per l'app Web.
appsettings.json
Annotazioni
Alcune organizzazioni non consentono segreti client. Per altre alternative, vedere Credenziali del certificato o Autenticazione senza certificato.
Per altre informazioni, vedere Guida introduttiva: Registrare un'applicazione.
Passaggio 3: Aggiornare la configurazione
Dopo aver creato le registrazioni dell'app, aggiornare i appsettings.json file:
API (MyService.ApiService/appsettings.json):
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "YOUR_TENANT_ID",
"ClientId": "YOUR_API_CLIENT_ID",
"Audiences": ["api://YOUR_API_CLIENT_ID"]
}
}
App Web (MyService.Web/appsettings.json):
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "YOUR_TENANT_ID",
"ClientId": "YOUR_WEB_CLIENT_ID",
"CallbackPath": "/signin-oidc",
"ClientCredentials": [
{ "SourceType": "ClientSecret" }
]
},
"WeatherApi": {
"Scopes": ["api://YOUR_API_CLIENT_ID/.default"]
}
}
Archiviare il segreto in modo sicuro:
cd MyService.Web
dotnet user-secrets set "AzureAd:ClientCredentials:0:ClientSecret" "YOUR_SECRET_VALUE"
| Valore | Dove trovare |
|---|---|
TenantId |
Panoramica Interfaccia di amministrazione di Microsoft Entra >> ID tenant |
API ClientId |
ID applicazione Registrazioni applicazioni > MyService.ApiService > Applicazione (cliente) |
Web ClientId |
Registrazioni app > MyService.Web > ID dell'applicazione (client) |
Client Secret |
Creato nel passaggio 2 (copia immediatamente al momento della creazione) |
Annotazioni
Il modello di avvio Aspire crea automaticamente una classe WeatherApiClient nel progetto MyService.Web. Questo HttpClient tipizzato viene usato in questa guida per dimostrare di chiamare l'API protetta. Non è necessario creare manualmente questa classe, che fa parte del modello.
Inizia subito
Questa sezione fornisce un riferimento condensato per l'aggiunta dell'autenticazione. Per procedure dettagliate, vedere Parte 1 e Parte 2.
API (MyService.ApiService)
Installare il pacchetto NuGet Microsoft.Identity.Web:
dotnet add package Microsoft.Identity.Web
Aggiungere la configurazione di Microsoft Entra a appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<tenant-id>",
"ClientId": "<api-client-id>",
"Audiences": ["api://<api-client-id>"]
}
}
Registrare l'autenticazione e l'autorizzazione in Program.cs:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization();
// ...
app.UseAuthentication();
app.UseAuthorization();
// ...
app.MapGet("/weatherforecast", () => { /* ... */ }).RequireAuthorization();
App Web (MyService.Web)
Installare il pacchetto NuGet Microsoft.Identity.Web:
dotnet add package Microsoft.Identity.Web
Aggiungere la configurazione di Microsoft Entra a appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<tenant-id>",
"ClientId": "<web-client-id>",
"CallbackPath": "/signin-oidc",
"ClientCredentials": [{ "SourceType": "ClientSecret" }]
},
"WeatherApi": { "Scopes": ["api://<api-client-id>/.default"] }
}
Configurare l'autenticazione, l'acquisizione di token e il client API downstream in Program.cs:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<BlazorAuthenticationChallengeHandler>();
builder.Services.AddHttpClient<WeatherApiClient>(client =>
client.BaseAddress = new("https+http://apiservice"))
.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));
// ...
app.UseAuthentication();
app.UseAuthorization();
app.MapGroup("/authentication").MapLoginAndLogout();
Acquisisce MicrosoftIdentityMessageHandler e collega automaticamente i token e BlazorAuthenticationChallengeHandler gestisce i problemi di consenso e accesso condizionale.
Importante
Non dimenticare di creare UserInfo.razor per il pulsante di accesso. Per informazioni dettagliate, vedere Aggiungere componenti dell'interfaccia utente Blazor .
Annotazioni
BlazorAuthenticationChallengeHandler e LoginLogoutEndpointRouteBuilderExtensions sono inclusi in Microsoft.Identity.Web (v3.3.0+). Non è necessaria alcuna copia di file.
Identificare i file da modificare
La tabella seguente elenca i file modificati in ogni progetto:
| Progetto | File | Changes |
|---|---|---|
| ApiService | Program.cs |
Autenticazione JWT Bearer, middleware di autorizzazione |
appsettings.json |
configurazione Microsoft Entra | |
.csproj |
Aggiungere Microsoft.Identity.Web |
|
| Web | Program.cs |
Autenticazione OIDC, acquisizione di token, BlazorAuthenticationChallengeHandler |
appsettings.json |
Microsoft Entra config, ambiti dell'API a valle | |
.csproj |
Aggiungere Microsoft.Identity.Web (v3.3.0+) |
|
Components/UserInfo.razor |
Interfaccia utente pulsante di accesso (nuovo file) | |
Components/Layout/MainLayout.razor |
Includere il componente UserInfo | |
Components/Routes.razor |
AuthorizeRouteView per le pagine protette | |
| Pagine che chiamano le API | Provare/intercettare con ChallengeHandler |
Informazioni sul flusso di autenticazione
Il diagramma seguente mostra come interagiscono il front-end Blazor, Microsoft Entra e l'API protetta:
flowchart LR
A[User Browser] -->|1 Login OIDC| B[Blazor Server<br/>MyService.Web]
B -->|2 Redirect| C[Microsoft Entra ID]
C -->|3 auth code| B
B -->|4 exchange auth code| C
C -->|5 tokens| B
B -->|6 cookie + session| A
B -->|7 HTTP + Bearer token| D[ASP.NET API<br/>MyService.ApiService<br/>Microsoft.Identity.Web]
D -->|8 Validate JWT| C
D -->|9 Weather data| B
- L'utente visita l'app Blazor → Non autenticato → vede il pulsante "Login".
-
Utente seleziona Login → Viene reindirizzato a
/authentication/login→ Sfida OIDC → Microsoft Entra. -
Utente accede → Microsoft Entra reindirizza
/signin-oidc→ cookie impostato. -
L'utente passa alla pagina Meteo → Blazor chiama
WeatherApiClient.GetAsync(). -
MicrosoftIdentityMessageHandlerintercetta la richiesta, acquisisce un token dalla cache (o aggiorna automaticamente) e allega l'intestazioneAuthorization: Bearer <token>. - API riceve la richiesta → Microsoft. Identity.Web convalida l'→ JWT restituisce i dati.
- Blazor esegue il rendering dei dati meteo.
Esaminare la struttura della soluzione
Modello di avvio Aspire crea il seguente layout di progetto:
MyService/
├── MyService.AppHost/ # Aspire orchestration
├── MyService.ApiService/ # Protected API (Microsoft.Identity.Web)
├── MyService.Web/ # Blazor Server (Microsoft.Identity.Web)
├── MyService.ServiceDefaults/ # Shared defaults
└── MyService.Tests/ # Tests
Parte 1: Proteggere il back-end dell'API con Microsoft. Identity.Web
Questa sezione configura il progetto API per convalidare i token di connessione JWT rilasciati da Microsoft Entra.
Aggiungere il pacchetto Microsoft.Identity.Web
Eseguire il comando seguente per installare il pacchetto NuGet Microsoft.Identity.Web:
cd MyService.ApiService
dotnet add package Microsoft.Identity.Web
Configurare le impostazioni di Microsoft Entra
Aggiungere la configurazione di Microsoft Entra a MyService.ApiService/appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<your-tenant-id>",
"ClientId": "<your-api-client-id>",
"Audiences": [
"api://<your-api-client-id>"
]
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Proprietà chiave:
-
ClientId: ID registrazione dell'app per le API Microsoft Entra -
TenantId: ID del tenant Microsoft Entra, oppure"organizations"per multi-tenant, oppure"common"per qualsiasi account Microsoft -
Audiences: destinatari validi del token (in genere l'URI dell'ID dell'app)
Aggiornare Program.cs API
Sostituire il contenuto di MyService.ApiService/Program.cs con il codice seguente per aggiungere l'autenticazione JWT Bearer e proteggere gli endpoint:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
// Add Microsoft.Identity.Web JWT Bearer authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddProblemDetails();
builder.Services.AddOpenApi();
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseExceptionHandler();
app.UseAuthentication();
app.UseAuthorization();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
string[] summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"];
app.MapGet("/", () =>
"API service is running. Navigate to /weatherforecast to see sample data.");
app.MapGet("/weatherforecast", () =>
{
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;
})
.WithName("GetWeatherForecast")
.RequireAuthorization();
app.MapDefaultEndpoints();
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Modifiche chiave:
- Registrare l'autenticazione Bearer JWT con
AddMicrosoftIdentityWebApi - Aggiungere
app.UseAuthentication()eapp.UseAuthorization()middleware - Applicare
.RequireAuthorization()agli endpoint protetti
Testare l'API protetta
Verificare che l'API rifiuti le richieste non autenticate e accetti token validi.
Inviare una richiesta senza token:
curl https://localhost:<PORT>/weatherforecast
# Expected: 401 Unauthorized
Inviare una richiesta con un token valido:
curl -H "Authorization: Bearer <TOKEN>" https://localhost:<PORT>/weatherforecast
# Expected: 200 OK with weather data
Parte 2: Configurare il front-end Blazor per l'autenticazione
L'app Blazor Server usa Microsoft. Identity.Web per:
- Accedi agli utenti con OIDC
- Acquisire i token di accesso per chiamare l'API
- Collegare token a richieste HTTP in uscita
Aggiungere il pacchetto Microsoft.Identity.Web
Eseguire il comando seguente per installare il pacchetto NuGet Microsoft.Identity.Web.
cd MyService.Web
dotnet add package Microsoft.Identity.Web
Configurare le impostazioni di Microsoft Entra
Aggiungere gli ambiti di configurazione Microsoft Entra e le API "downstream" a MyService.Web/appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "<your-tenant>.onmicrosoft.com",
"TenantId": "<tenant-guid>",
"ClientId": "<web-app-client-id>",
"CallbackPath": "/signin-oidc",
"ClientCredentials": [
{
"SourceType": "ClientSecret",
"ClientSecret": "<your-client-secret>"
}
]
},
"WeatherApi": {
"Scopes": [ "api://<api-client-id>/.default" ]
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Dettagli di configurazione:
-
ClientId: ID registrazione app Web (non ID API) -
ClientCredentials: credenziali per l'app Web per acquisire i token. Supporta più tipi di credenziali. Consulta Panoramica delle credenziali per le opzioni pronte per l'uso in produzione. -
Scopes: deve corrispondere all'URI ID dell'app dell'API con il suffisso/.default
Avvertimento
Per l'ambiente di produzione, usare certificati o identità gestita anziché segreti client. Per l'approccio consigliato, vedere Autenticazione senza certificati.
Aggiornare il Program.cs dell'applicazione web
Sostituire il contenuto di MyService.Web/Program.cs con il codice seguente per configurare l'autenticazione OIDC, l'acquisizione di token e il client dell'API downstream:
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;
using MyService.Web;
using MyService.Web.Components;
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
// Authentication + Microsoft Identity Web
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddCascadingAuthenticationState();
// Blazor components
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
// Blazor authentication challenge handler for incremental consent and Conditional Access
builder.Services.AddScoped<BlazorAuthenticationChallengeHandler>();
builder.Services.AddOutputCache();
// Downstream API client with MicrosoftIdentityMessageHandler
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
// Aspire service discovery: resolves "apiservice" at runtime
client.BaseAddress = new("https+http://apiservice");
})
.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.UseOutputCache();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
// Login/Logout endpoints with incremental consent support
app.MapGroup("/authentication").MapLoginAndLogout();
app.MapDefaultEndpoints();
app.Run();
Punti principali:
-
AddMicrosoftIdentityWebApp: configura l'autenticazione OIDC -
EnableTokenAcquisitionToCallDownstreamApi: abilita l'acquisizione di token per le API downstream -
AddScoped<BlazorAuthenticationChallengeHandler>: gestisce il consenso incrementale e l'accesso condizionale nel server Blazor -
AddMicrosoftIdentityMessageHandler: collega automaticamente i token portatori alle richieste HttpClient -
https+http://apiservice: il rilevamento del servizio Aspire lo risolve nell'URL effettivo dell'API -
Ordine del middleware:
UseAuthentication()→UseAuthorization()→ endpoint
L'estensione AddMicrosoftIdentityMessageHandler supporta più modelli di configurazione:
Opzione 1: Configurazione da appsettings.json (illustrato in precedenza)
.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));
Opzione 2: Configurazione inline con delegato Action
.AddMicrosoftIdentityMessageHandler(options =>
{
options.Scopes.Add("api://<api-client-id>/.default");
});
Opzione 3: configurazione per richiesta (senza parametri)
.AddMicrosoftIdentityMessageHandler();
// Then in your service, configure per-request:
var request = new HttpRequestMessage(HttpMethod.Get, "/weatherforecast")
.WithAuthenticationOptions(options =>
{
options.Scopes.Add("api://<api-client-id>/.default");
});
var response = await _httpClient.SendAsync(request);
Aggiungere componenti dell'interfaccia utente Blazor
Importante
Questo passaggio viene spesso dimenticato. Senza il componente UserInfo, gli utenti non possono accedere.
BlazorAuthenticationChallengeHandler e LoginLogoutEndpointRouteBuilderExtensions sono inclusi in Microsoft.Identity.Web v3.3.0+. Sono disponibili automaticamente dopo aver fatto riferimento al pacchetto. Non è necessaria alcuna copia di file.
Creare MyService.Web/Components/UserInfo.razor:
@using Microsoft.AspNetCore.Components.Authorization
<AuthorizeView>
<Authorized>
<span class="nav-item">Hello, @context.User.Identity?.Name</span>
<form action="/authentication/logout" method="post" class="nav-item">
<AntiforgeryToken />
<input type="hidden" name="returnUrl" value="/" />
<button type="submit" class="btn btn-link nav-link">Logout</button>
</form>
</Authorized>
<NotAuthorized>
<a href="/authentication/login?returnUrl=/" class="nav-link">Login</a>
</NotAuthorized>
</AuthorizeView>
Aggiungi al layout: Includi <UserInfo /> nel MainLayout.razor:
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<UserInfo />
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>
Aggiornare Routes.razor per AuthorizeRouteView
Sostituire RouteView con AuthorizeRouteView in Components/Routes.razor:
@using Microsoft.AspNetCore.Components.Authorization
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
<NotAuthorized>
<p>You are not authorized to view this page.</p>
<a href="/authentication/login">Login</a>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
Gestire le eccezioni nelle pagine che chiamano le API
Blazor Server richiede la gestione esplicita delle eccezioni per l'accesso condizionale e il consenso. Devi gestire MicrosoftIdentityWebChallengeUserException in ogni pagina che chiama un'API downstream, a meno che l'app non sia pre-autorizzata e richiedi tutti gli ambiti in anticipo in Program.cs.
L'esempio seguente Weather.razor illustra la gestione corretta delle eccezioni:
@page "/weather"
@attribute [Authorize]
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Identity.Web
@inject WeatherApiClient WeatherApi
@inject BlazorAuthenticationChallengeHandler ChallengeHandler
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
@if (!string.IsNullOrEmpty(errorMessage))
{
<div class="alert alert-warning">@errorMessage</div>
}
else if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
private string? errorMessage;
protected override async Task OnInitializedAsync()
{
if (!await ChallengeHandler.IsAuthenticatedAsync())
{
await ChallengeHandler.ChallengeUserWithConfiguredScopesAsync("WeatherApi:Scopes");
return;
}
try
{
forecasts = await WeatherApi.GetWeatherAsync();
}
catch (Exception ex)
{
// Handle incremental consent / Conditional Access
if (!await ChallengeHandler.HandleExceptionAsync(ex))
{
errorMessage = $"Error loading weather data: {ex.Message}";
}
}
}
}
Il modello funziona come segue:
-
IsAuthenticatedAsync()controlla se l'utente ha eseguito l'accesso prima di effettuare chiamate API. -
HandleExceptionAsync()catchesMicrosoftIdentityWebChallengeUserException(in qualità di InnerException). - Se si tratta di un'eccezione di sfida, l'utente viene reindirizzato per ripetere l'autenticazione con le attestazioni o gli ambiti necessari.
- Se non si tratta di un'eccezione di sfida,
HandleExceptionAsyncrestituiscefalsecosì puoi gestire l'errore da solo.
Archiviare il segreto client nei segreti utente
Usare .NET Secret Manager per archiviare il segreto client in modo sicuro durante lo sviluppo.
Attenzione
Non mettere mai segreti sotto il controllo del codice sorgente.
Inizializzare dei segreti utente e archiviare il segreto del client:
cd MyService.Web
dotnet user-secrets init
dotnet user-secrets set "AzureAd:ClientCredentials:0:ClientSecret" "<your-client-secret>"
Quindi, aggiornare appsettings.json per rimuovere il segreto hardcoded.
{
"AzureAd": {
"ClientCredentials": [
{
"SourceType": "ClientSecret"
}
]
}
}
Microsoft. Identity.Web supporta più tipi di credenziali. Per la produzione, vedere Panoramica delle credenziali.
Verificare l'implementazione
Usare questo elenco di controllo per confermare il completamento di tutti i passaggi necessari.
Progetto API
- [ ] Aggiunto pacchetto
Microsoft.Identity.Web - [ ] Aggiornato
appsettings.jsoncon la sezioneAzureAd - [ ] Aggiornato
Program.csconAddMicrosoftIdentityWebApi - [ ] Aggiunta
.RequireAuthorization()agli endpoint protetti
Progetto Web/Blazor
- [ ] Pacchetto
Microsoft.Identity.Webaggiunto (v3.3.0+) - [ ] Aggiornato
appsettings.jsoncon le sezioniAzureAdeWeatherApi - [ ] Aggiornato
Program.cscon OIDC, acquisizione di token - [ ] Aggiunta
AddScoped<BlazorAuthenticationChallengeHandler>() - [ ] Creato
Components/UserInfo.razor(pulsante di login) - [ ] Aggiornato
MainLayout.razorper includere<UserInfo /> - [ ] Aggiornato
Routes.razorconAuthorizeRouteView - [ ] Aggiunto try/catch con
ChallengeHandlerin ogni pagina che effettua chiamate alle API - [ ] Segreto del cliente archiviato nei segreti dell'utente
Verifica
- [ ]
dotnet buildha successo - [ ] Registrazioni app create nel centro di amministrazione Microsoft Entra
- [ ]
appsettings.jsonha GUID autentici (nessun segnaposto)
Testare e risolvere i problemi
Dopo aver completato l'implementazione, eseguire l'applicazione e verificare il flusso di autenticazione end-to-end.
Eseguire l'applicazione
Avviare Aspire AppHost per avviare entrambi i progetti Web e API.
# From solution root
dotnet restore
dotnet build
# Launch AppHost (starts both Web and API)
dotnet run --project .\MyService.AppHost\MyService.AppHost.csproj
Testare il flusso di autenticazione
- Aprire il browser → Blazor Web UI (consultare Aspire dashboard per l'URL).
- Selezionare Login → Accedi con Microsoft Entra.
- Passare alla pagina Meteo .
- Verificare i caricamenti dei dati meteo (dall'API protetta).
Risolvere i problemi comuni
La tabella seguente elenca i problemi frequenti e le relative soluzioni:
| Issue | Soluzione |
|---|---|
| 401 sulle chiamate API | Verificare che gli ambiti in appsettings.json coincidano con l'URI dell'ID app dell'API. |
| Reindirizzamento OIDC fallito | Aggiungere /signin-oidc agli URI di reindirizzamento Microsoft Entra |
| Token non collegato | Assicurarsi che AddMicrosoftIdentityMessageHandler venga chiamato su HttpClient |
| L'individuazione del servizio non riesce | Controllare AppHost.cs i riferimenti sia per i progetti e verificare che siano in esecuzione. |
| AADSTS65001 | Consenso amministratore obbligatorio: concedere il consenso nel Interfaccia di amministrazione di Microsoft Entra |
| Nessun pulsante di accesso | Assicurarsi che UserInfo.razor esista e sia incluso in MainLayout.razor |
| Ciclo di consenso | Assicurarsi che il blocco try/catch con HandleExceptionAsync sia usato in tutte le pagine che richiamano l'API |
Abilitare la registrazione MSAL
Quando si risolvano i problemi di autenticazione, abilitare la registrazione MSAL dettagliata per visualizzare i dettagli dell'acquisizione dei token. Aggiungere i livelli di log seguenti a appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Identity": "Debug",
"Microsoft.IdentityModel": "Debug"
}
}
}
Avvertimento
Disabilitare la registrazione di debug nell'ambiente di produzione perché può essere molto verbosa.
Ispezionare i token
Per eseguire il debug dei problemi del token, decodificare il token JWT in jwt.ms e verificare:
-
aud(audience): corrisponde al Client ID dell'API o all'URI del ID App -
iss(issuer): corrisponde al tuo tenant (https://login.microsoftonline.com/<tenant-id>/v2.0) -
scp(ambiti): contiene gli ambiti necessari -
exp(scadenza): Il token non è scaduto
Esplorare scenari comuni
Le sezioni seguenti illustrano come estendere l'implementazione di base per casi d'uso aggiuntivi.
Proteggere le pagine Blazor
Aggiungere l'attributo [Authorize] alle pagine che richiedono l'autenticazione:
@page "/weather"
@attribute [Authorize]
In alternativa, definire i criteri di autorizzazione in Program.cs:
// Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});
@attribute [Authorize(Policy = "AdminOnly")]
Convalidare gli ambiti nell'API
Assicurarsi che l'API accetti solo token con ambiti specifici concatenando RequireScope:
app.MapGet("/weatherforecast", () =>
{
// ... implementation
})
.RequireAuthorization()
.RequireScope("access_as_user");
Usare token solo per app (da servizio a servizio)
Per scenari daemon o chiamate da servizio a servizio senza contesto utente, impostare RequestAppToken su true:
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
client.BaseAddress = new("https+http://apiservice");
})
.AddMicrosoftIdentityMessageHandler(options =>
{
options.Scopes.Add("api://<api-client-id>/.default");
options.RequestAppToken = true;
});
Usa credenziali senza certificato nell'ambiente di produzione
Per le distribuzioni di produzione in Azure, usare l'identità gestita anziché i segreti client. Configurare la ClientCredentials sezione come indicato di seguito:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<tenant-guid>",
"ClientId": "<web-app-client-id>",
"ClientCredentials": [
{
"SourceType": "SignedAssertionFromManagedIdentity",
"ManagedIdentityClientId": "<user-assigned-mi-client-id>"
}
]
}
}
Per altre informazioni, vedere Autenticazione senza certificati.
Chiamare le API downstream dall'API (per conto di)
Se l'API deve chiamare un'altra API downstream per conto dell'utente, abilita l'acquisizione del token "on-behalf-of" in Program.cs.
// MyService.ApiService/Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddDownstreamApi("GraphApi", builder.Configuration.GetSection("GraphApi"));
Aggiungere la configurazione dell'API downstream a appsettings.json:
{
"GraphApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": [ "User.Read" ]
}
}
Chiamare quindi l'API downstream da un endpoint:
{
var user = await downstreamApi.GetForUserAsync<JsonElement>("GraphApi", "me");
return user;
}).RequireAuthorization();
Per altre informazioni, vedere Chiamata di API downstream.