Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym artykule zaimplementujesz autoryzację w ASP.NET Core webowych interfejsach API przy użyciu Microsoft.Identity.Web. Sprawdzisz zakresy (uprawnienia delegowane) i uprawnienia aplikacji (uprawnienia aplikacji), aby kontrolować dostęp do chronionych zasobów. W przykładach użyto Microsoft Entra ID jako dostawcy tożsamości.
Omówienie pojęć związanych z autoryzacją
W tej sekcji opisano kluczowe różnice między uwierzytelnianiem a autoryzacją oraz co Microsoft.Identity.Web weryfikuje w tokenach dostępu.
Uwierzytelnianie a autoryzacja
| Pojęcie | Purpose | Wynik |
|---|---|---|
| Uwierzytelnianie | Weryfikowanie tożsamości | 401 Brak autoryzacji w przypadku niepowodzenia |
| Autoryzacja | Weryfikowanie uprawnień | 403 Zabronione, jeśli niewystarczające |
Co zostanie zweryfikowane
Gdy internetowy interfejs API odbiera token dostępu, Microsoft. Identity.Web weryfikuje:
- Podpis tokenu — czy pochodzi z zaufanego urzędu?
- Docelowy odbiorca tokenu — czy jest przeznaczony dla tego interfejsu API?
- Wygaśnięcie tokenu — czy nadal jest ono prawidłowe?
- Zakresy/role — czy aplikacja kliencka i temat (użytkownik) mają odpowiednie uprawnienia?
Ten przewodnik koncentruje się na 4 — weryfikowaniu zakresów i uprawnień aplikacji.
Zakresy (uprawnienia delegowane)
Zakresy mają zastosowanie, gdy użytkownik deleguje uprawnienia do aplikacji do działania w ich imieniu (na przykład internetowy interfejs API wywoływany w imieniu zalogowanego użytkownika).
| Szczegół | Wartość |
|---|---|
| Żądanie tokenu |
scp lub scope (aplikacja kliencka); roles (użytkownik) |
| Przykładowe wartości |
"access_as_user", "User.Read", "Files.ReadWrite" |
Uprawnienia aplikacji
Uprawnienia aplikacji mają zastosowanie, gdy aplikacja wywołuje internetowy interfejs API jako sam bez kontekstu użytkownika, na przykład demona lub usługi w tle przy użyciu poświadczeń klienta.
| Szczegół | Wartość |
|---|---|
| Żądanie tokenu | roles |
| Przykładowe wartości |
"Mail.Read.All", "User.Read.All" |
Weryfikowanie zakresów za pomocą elementu RequiredScope
Atrybut RequiredScope sprawdza, czy token dostępu zawiera co najmniej jeden z określonych zakresów. Użyj tego atrybutu, gdy interfejs API obsługuje tylko żądania delegowane przez użytkownika.
Konfigurowanie weryfikacji zakresu
Wykonaj następujące kroki, aby włączyć walidację zakresu w interfejsie API.
1. Włącz autoryzację w interfejsie API:
Dodaj usługi uwierzytelniania i autoryzacji do potoku aplikacji:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(); // Required for authorization
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization(); // Must be after UseAuthentication
app.MapControllers();
app.Run();
2. Ochrona kontrolerów lub akcji:
Zastosuj atrybuty [Authorize] i [RequiredScope] do swojego kontrolera lub poszczególnych działań:
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web.Resource;
[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
[HttpGet]
public IActionResult GetTodos()
{
// Only accessible if token has "access_as_user" scope
return Ok(new[] { "Todo 1", "Todo 2" });
}
}
Zastosuj wzorce zakresu
Wybierz wzorzec, który najlepiej pasuje do sposobu zarządzania zakresami w aplikacji.
Wzorzec 1. Zakresy zakodowane na stałe
Użyj tego wzorca, gdy zakresy są stałe i znane w czasie programowania.
[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
// All actions require "access_as_user" scope
}
Aby zaakceptować jeden z wielu zakresów, wyświetl je jako parametry:
[Authorize]
[RequiredScope("read", "write", "admin")]
public class TodoListController : ControllerBase
{
// Token must have "read" OR "write" OR "admin"
}
Wzorzec 2. Zakresy zdefiniowane w konfiguracji
Użyj tego wzorca, gdy zakresy powinny być konfigurowalne dla poszczególnych środowisk. Zdefiniuj zakresy w pliku konfiguracji:
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-api-client-id",
"Scopes": "access_as_user read write"
}
}
Odwołaj się do klucza konfiguracji w kontrolerze:
[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : ControllerBase
{
// Scopes read from configuration
}
Takie podejście umożliwia zmianę zakresów bez konieczności ponownego kompilowania.
Wzorzec 3. Zakresy na poziomie akcji
Użyj tego wzorca, gdy różne akcje wymagają różnych uprawnień. Zastosuj [RequiredScope] do poszczególnych metod akcji:
[Authorize]
public class TodoListController : ControllerBase
{
[HttpGet]
[RequiredScope("read")]
public IActionResult GetTodos()
{
return Ok(todos);
}
[HttpPost]
[RequiredScope("write")]
public IActionResult CreateTodo([FromBody] Todo todo)
{
// Only tokens with "write" scope can create
return CreatedAtAction(nameof(GetTodos), todo);
}
[HttpDelete("{id}")]
[RequiredScope("admin")]
public IActionResult DeleteTodo(int id)
{
// Only tokens with "admin" scope can delete
return NoContent();
}
}
Omówienie przepływu weryfikacji
Po nadejściu żądania oprogramowanie pośredniczące przetwarza je w następującej kolejności:
- ASP.NET Core oprogramowanie pośredniczące uwierzytelniania weryfikuje token
-
RequiredScopesprawdzanie atrybutów dlascplubscopeoświadczenia - Jeśli token zawiera co najmniej jeden pasujący zakres, żądanie będzie kontynuowane.
- Jeśli nie zostanie znaleziony pasujący zakres, interfejs API zwróci odpowiedź 403 Zabronione.
W poniższym przykładzie przedstawiono typową odpowiedź na błąd:
{
"error": "insufficient_scope",
"error_description": "The token does not have the required scope 'access_as_user'."
}
Weryfikowanie uprawnień aplikacji za pomocą elementu RequiredScopeOrAppPermission
Atrybut RequiredScopeOrAppPermissionweryfikuje zakresy (delegowane) lub uprawnienia aplikacji (aplikacja ). Użyj tego atrybutu, gdy interfejs API obsługuje zarówno aplikacje delegowane przez użytkownika, jak i aplikacje demona/usługi z tego samego punktu końcowego.
Jeśli interfejs API obsługuje tylko żądania delegowane przez użytkownika, użyj RequiredScope zamiast tego.
Konfigurowanie walidacji uprawnień zakresu lub aplikacji
Zastosuj atrybut, aby zaakceptować dowolny typ tokenu:
using Microsoft.Identity.Web.Resource;
[Authorize]
[RequiredScopeOrAppPermission(
AcceptedScope = new[] { "access_as_user" },
AcceptedAppPermission = new[] { "TodoList.ReadWrite.All" }
)]
public class TodoListController : ControllerBase
{
[HttpGet]
public IActionResult GetTodos()
{
// Accessible with EITHER:
// - User-delegated token with "access_as_user" scope, OR
// - App-only token with "TodoList.ReadWrite.All" app permission
return Ok(todos);
}
}
Konfigurowanie uprawnień aplikacji z ustawień
Przechowywanie zakresów i uprawnień aplikacji w konfiguracji w celu ich zmiany bez ponownej kompilacji.
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-api-client-id",
"Scopes": "access_as_user",
"AppPermissions": "TodoList.ReadWrite.All TodoList.Admin"
}
}
Odwołaj się do kluczy konfiguracji w kontrolerze:
[Authorize]
[RequiredScopeOrAppPermission(
RequiredScopesConfigurationKey = "AzureAd:Scopes",
RequiredAppPermissionsConfigurationKey = "AzureAd:AppPermissions"
)]
public class TodoListController : ControllerBase
{
// Scopes and app permissions from configuration
}
Porównanie różnic w roszczeniach tokenu
W poniższej tabeli przedstawiono różnice między oświadczeniami delegowanymi przez użytkownika i tokenami tylko dla aplikacji:
| Typ tokenu | Roszczenie | Przykładowa wartość |
|---|---|---|
| Delegowane przez użytkownika |
scp lub scope |
"access_as_user User.Read" |
| Tylko aplikacja | roles |
["TodoList.ReadWrite.All"] |
W poniższym przykładzie pokazano token delegowany przez użytkownika:
{
"aud": "api://your-api-client-id",
"iss": "https://login.microsoftonline.com/.../v2.0",
"scp": "access_as_user",
"sub": "user-object-id",
...
}
W poniższym przykładzie przedstawiono token tylko dla aplikacji:
{
"aud": "api://your-api-client-id",
"iss": "https://login.microsoftonline.com/.../v2.0",
"roles": ["TodoList.ReadWrite.All"],
"sub": "app-object-id",
...
}
Tworzenie zasad autoryzacji
W przypadku złożonych scenariuszy autoryzacji należy użyć zasad autoryzacji ASP.NET Core. Zasady umożliwiają scentralizowanie reguł, łączenie wielu wymagań i pisanie logiki autoryzacji z możliwością testowania.
| Benefit | Opis |
|---|---|
| Scentralizowana logika | Definiowanie reguł autoryzacji raz, ponowne użycie wszędzie |
| Komponowalne | Łączenie wielu wymagań (zakresy i oświadczenia + logika niestandardowa) |
| Testowalny | Łatwiejsze testowanie jednostkowe logiki autoryzacji |
| Elastyczny | Wymagania niestandardowe wykraczające poza walidację zakresu |
Wzorzec 1. Definiowanie zasad za pomocą elementu RequireScope
Zdefiniuj nazwane zasady, które wymagają określonych zakresów, a następnie odwołaj się do nich na kontrolerach:
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("TodoReadPolicy", policyBuilder =>
{
policyBuilder.RequireScope("read", "access_as_user");
});
options.AddPolicy("TodoWritePolicy", policyBuilder =>
{
policyBuilder.RequireScope("write", "admin");
});
});
var app = builder.Build();
Zastosuj zasady do akcji kontrolera:
[Authorize]
public class TodoListController : ControllerBase
{
[HttpGet]
[Authorize(Policy = "TodoReadPolicy")]
public IActionResult GetTodos()
{
return Ok(todos);
}
[HttpPost]
[Authorize(Policy = "TodoWritePolicy")]
public IActionResult CreateTodo([FromBody] Todo todo)
{
return CreatedAtAction(nameof(GetTodos), todo);
}
}
Wzorzec 2. Definiowanie zasad za pomocą elementu ScopeAuthorizationRequirement
Użyj polecenia ScopeAuthorizationRequirement , aby uzyskać bardziej jawne wymagania dotyczące zakresu:
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.Resource;
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CustomPolicy", policyBuilder =>
{
policyBuilder.AddRequirements(
new ScopeAuthorizationRequirement(new[] { "access_as_user" })
);
});
});
Wzorzec 3. Ustawianie zasad domyślnych
Ustaw domyślne zasady, które mają zastosowanie do wszystkich [Authorize] atrybutów automatycznie:
builder.Services.AddAuthorization(options =>
{
var defaultPolicy = new AuthorizationPolicyBuilder()
.RequireScope("access_as_user")
.Build();
options.DefaultPolicy = defaultPolicy;
});
Teraz każdy [Authorize] atrybut wymaga access_as_user zakresu.
[Authorize] // Automatically requires "access_as_user" scope
public class TodoListController : ControllerBase
{
// All actions protected by default policy
}
Wzorzec 4. Łączenie wielu wymagań
Łączenie wymagań dotyczących zakresu, roli i uwierzytelniania w ramach jednej zasady:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policyBuilder =>
{
policyBuilder.RequireScope("admin");
policyBuilder.RequireRole("Admin"); // Also check role claim
policyBuilder.RequireAuthenticatedUser();
});
});
Wzorzec 5. Tworzenie zasad z konfiguracji
Zachowaj specyficzne dla środowiska zasady poprzez ładowanie zakresów z konfiguracji.
var requiredScopes = builder.Configuration["AzureAd:Scopes"]?.Split(' ');
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ApiAccessPolicy", policyBuilder =>
{
if (requiredScopes != null)
{
policyBuilder.RequireScope(requiredScopes);
}
});
});
Filtrowanie żądań według najemcy
Ogranicz dostęp interfejsu API do tokenów z określonych dzierżaw Microsoft Entra. Jest to przydatne, gdy interfejs API dla wielu dzierżawców powinien akceptować tylko żądania od zatwierdzonych dzierżawców klientów.
Ograniczenie dostępu tylko dla dozwolonych dzierżawców
Zdefiniuj zasady sprawdzające oświadczenie identyfikatora dzierżawy względem listy dozwolonych:
builder.Services.AddAuthorization(options =>
{
string[] allowedTenants =
{
"14c2f153-90a7-4689-9db7-9543bf084dad", // Contoso tenant
"af8cc1a0-d2aa-4ca7-b829-00d361edb652", // Fabrikam tenant
"979f4440-75dc-4664-b2e1-2cafa0ac67d1" // Northwind tenant
};
options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
{
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants
);
});
// Apply to all endpoints by default
options.DefaultPolicy = options.GetPolicy("AllowedTenantsOnly");
});
Konfiguracja filtrowania tenanta w ustawieniach
Przechowuj dozwolone identyfikatory najemców w konfiguracji, aby zarządzać nimi bez zmian w kodzie.
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "your-api-client-id",
"AllowedTenants": [
"14c2f153-90a7-4689-9db7-9543bf084dad",
"af8cc1a0-d2aa-4ca7-b829-00d361edb652"
]
}
}
Przeczytaj listę dzierżaw i utwórz zasady podczas uruchamiania:
var allowedTenants = builder.Configuration.GetSection("AzureAd:AllowedTenants")
.Get<string[]>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
{
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants ?? Array.Empty<string>()
);
});
});
Łączenie zakresów z filtrowaniem klientów
Utwórz zasady wymagające zarówno prawidłowego zakresu, jak i zatwierdzonej dzierżawy:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("SecureApiAccess", policyBuilder =>
{
// Require specific scope
policyBuilder.RequireScope("access_as_user");
// AND require specific tenant
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants
);
});
});
Postępuj zgodnie z najlepszymi rozwiązaniami
Zastosuj te zalecenia, aby utworzyć bezpieczną, konserwalną logikę autoryzacji.
Rzeczy, które należy robić
1. Zawsze paruj [Authorize] z walidacją zakresu:
[Authorize] // Authentication
[RequiredScope("access_as_user")] // Authorization
public class MyController : ControllerBase { }
2. Użyj konfiguracji dla zakresów specyficznych dla środowiska:
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
3. Zastosuj najmniejsze uprawnienia:
[HttpGet]
[RequiredScope("read")] // Only read permission needed
[HttpPost]
[RequiredScope("write")] // Write permission for modifications
4. Użyj zasad do złożonej autoryzacji:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
{
policy.RequireScope("admin");
policy.RequireClaim("department", "IT");
});
});
5. Włącz szczegółowe odpowiedzi na błędy w trakcie tworzenia:
if (builder.Environment.IsDevelopment())
{
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
}
Czego nie robić
1. Nie pomijaj [Authorize] podczas używania RequiredScope:
// Wrong - RequiredScope won't work without [Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }
// Correct
[Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }
2. Nie koduj identyfikatorów najemców w środowisku produkcyjnym:
// Wrong
policyBuilder.RequireClaim("tid", "14c2f153-90a7-4689-9db7-9543bf084dad");
// Better - use configuration
var tenants = Configuration.GetSection("AllowedTenants").Get<string[]>();
policyBuilder.RequireClaim("tid", tenants);
3. Nie należy mylić zakresów z rolami:
// Wrong - This checks roles claim, not scopes
[RequiredScope("Admin")] // "Admin" is typically a role, not a scope
// Correct
[RequiredScope("access_as_user")] // Scope
[Authorize(Roles = "Admin")] // Role
4. Nie ujawniaj poufnych informacji o zakresie w komunikatach o błędach produkcyjnych:
Skonfiguruj odpowiednie poziomy rejestrowania i obsługę błędów dla środowisk produkcyjnych.
Rozwiązywanie problemów z autoryzacją
Skorzystaj z poniższych wskazówek, aby zdiagnozować typowe problemy z autoryzacją.
403 Zabronione — brak zakresu
Błąd: Interfejs API zwraca wartość 403 nawet z prawidłowym tokenem.
Diagnoza:
- Zdekoduj token na jwt.ms.
- Sprawdź
scplubscopeoświadczenie. - Sprawdź, czy wartość jest zgodna z atrybutem
RequiredScope.
Rozwiązanie:
- Upewnij się, że aplikacja kliencka żąda prawidłowego zakresu podczas uzyskiwania tokenu.
- Sprawdź, czy zakres jest ujawniony w rejestracji aplikacji API w Microsoft Entra.
- W razie potrzeby udziel zgody administratora.
RequiredScope nie działa
Objawem: Atrybut wydaje się być ignorowany.
Sprawdź:
- Czy dodano
[Authorize]atrybut? - Czy wywoływana jest
app.UseAuthorization()poapp.UseAuthentication()? - Czy jest
services.AddAuthorization()zarejestrowany?
Nie można odnaleźć klucza konfiguracji
Błąd: Sprawdzanie zakresu kończy się niepowodzeniem bez wyraźnego powiadomienia.
Sprawdź:
{
"AzureAd": {
"Scopes": "access_as_user" // Matches RequiredScopesConfigurationKey
}
}
Upewnij się, że ścieżka konfiguracji jest dokładnie zgodna.