Udostępnij za pośrednictwem


Zabezpieczanie mikrousług platformy .NET i aplikacji internetowych

Wskazówka

Ta treść jest fragmentem eBooka "Architektura mikrousług .NET dla konteneryzowanych aplikacji .NET", dostępnego na .NET Docs lub jako bezpłatny plik PDF do pobrania i czytania w trybie offline.

Miniatura okładki eBooka „Architektura mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET”.

Istnieje tak wiele aspektów zabezpieczeń w mikrousługach i aplikacjach internetowych, że temat mógłby łatwo zająć kilka książek takich jak ta. W tej sekcji skoncentrujemy się na uwierzytelnianiu, autoryzacji oraz tajnych elementach aplikacji.

Implementowanie uwierzytelniania w mikrousługach platformy .NET i aplikacjach internetowych

Często konieczne jest, aby zasoby i interfejsy API publikowane przez usługę ograniczały się do określonych zaufanych użytkowników lub klientów. Pierwszym krokiem do podejmowania tego rodzaju decyzji dotyczących zaufania na poziomie interfejsu API jest uwierzytelnianie. Uwierzytelnianie to proces niezawodnego weryfikowania tożsamości użytkownika.

W scenariuszach mikrousług uwierzytelnianie jest zwykle obsługiwane centralnie. Jeśli używasz bramy API, jest to dobre miejsce do uwierzytelnienia, jak pokazano na rysunku 9-1. Jeśli używasz tej metody, upewnij się, że nie można uzyskać bezpośredniego dostępu do poszczególnych mikrousług (bez bramy interfejsu API), chyba że zostaną spełnione dodatkowe zabezpieczenia w celu uwierzytelnienia komunikatów pochodzących z bramy.

Diagram przedstawiający sposób interakcji aplikacji mobilnej klienta z zapleczem.

Rysunek 9–1. Scentralizowane uwierzytelnianie za pomocą bramy interfejsu API

Gdy usługa API Gateway centralizuje uwierzytelnianie, dodaje informacje o użytkowniku podczas przekazywania żądań do mikrousług. Jeśli dostęp do usług można uzyskać bezpośrednio, usługa uwierzytelniania, np. Azure Active Directory lub dedykowana mikrousługa uwierzytelniania działająca jako usługa tokenu zabezpieczającego (STS), może służyć do uwierzytelniania użytkowników. Decyzje dotyczące zaufania są współużytkowane między usługami z tokenami zabezpieczającymi lub plikami cookie. (Te tokeny można udostępniać między aplikacjami platformy ASP.NET Core, w razie potrzeby, implementując udostępnianie plików cookie). Ten wzorzec przedstawiono na rysunku 9–2.

Diagram przedstawiający uwierzytelnianie za pośrednictwem mikrousług zaplecza.

Rysunek 9–2. Uwierzytelnianie przez mikrousługę tożsamości; zaufanie jest przekazywane przy użyciu tokenu autoryzacji

Gdy dostęp do mikrousług jest uzyskiwany bezpośrednio, zaufanie, które obejmuje uwierzytelnianie i autoryzację, jest obsługiwane przez token zabezpieczający wystawiony przez dedykowaną mikrousługę współdzieloną między mikrousługami.

Uwierzytelnianie za pomocą ASP.NET Core Identity

Podstawowym mechanizmem ASP.NET Core do identyfikowania użytkowników aplikacji jest system członkostwa w ASP.NET Core Identity . ASP.NET Core Identity przechowuje informacje o użytkowniku (w tym informacje o logowaniu, rolach i oświadczeniach) w magazynie danych skonfigurowanym przez dewelopera. Zazwyczaj magazyn danych ASP.NET Core Identity jest magazynem programu Entity Framework podanym w pakiecie Microsoft.AspNetCore.Identity.EntityFrameworkCore . Jednak sklepy niestandardowe lub inne pakiety innych firm mogą być używane do przechowywania informacji o tożsamości w usłudze Azure Table Storage, CosmosDB lub w innych lokalizacjach.

Wskazówka

ASP.NET Core 2.1 i nowszych udostępnia ASP.NET Core Identity jako bibliotekę klas Razor, więc w projekcie nie będzie widocznych zbyt wiele niezbędnego kodu, podobnie jak w przypadku poprzednich wersji. Aby uzyskać szczegółowe informacje na temat dostosowywania kodu tożsamości zgodnie z potrzebami, zobacz Tworzenie szkieletu tożsamości w projektach ASP.NET Core.

Poniższy kod jest pobierany z szablonu projektu MVC aplikacji internetowej ASP.NET Core z wybranym uwierzytelnianiem indywidualnego konta użytkownika. Pokazano w nim, jak skonfigurować tożsamość podstawową ASP.NET przy użyciu programu Entity Framework Core w pliku Program.cs .

//...
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(
        builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddDefaultIdentity<IdentityUser>(options =>
    options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();
//...

Po skonfigurowaniu ASP.NET Core Identity należy ją włączyć, dodając element app.UseAuthentication() i endpoints.MapRazorPages() , jak pokazano w poniższym kodzie w pliku Program.cs usługi:

//...
app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
});
//...

Ważne

Wiersze w poprzednim kodzie MUSZĄ BYĆ W KOLEJNOŚCI POKAZANEJ, aby Identity działało poprawnie.

Korzystanie z ASP.NET Core Identity umożliwia wykonanie kilku scenariuszy:

  • Utwórz nowe informacje o użytkowniku przy użyciu typu UserManager (userManager.CreateAsync).

  • Uwierzytelnianie użytkowników przy użyciu typu SignInManager. Możesz użyć signInManager.SignInAsync do bezpośredniego zalogowania się lub signInManager.PasswordSignInAsync potwierdzenia, że hasło użytkownika jest poprawne, a następnie zalogowania się.

  • Zidentyfikuj użytkownika na podstawie informacji przechowywanych w pliku cookie (odczytywanym przez oprogramowanie pośredniczące ASP.NET Core Identity), aby kolejne żądania z przeglądarki zawierały tożsamość i oświadczenia zalogowanego użytkownika.

ASP.NET Core Identity obsługuje również uwierzytelnianie dwuskładnikowe.

W przypadku scenariuszy uwierzytelniania, które korzystają z magazynu danych użytkownika lokalnego i utrwalania tożsamości między żądaniami przy użyciu plików cookie (co jest typowe w przypadku aplikacji internetowych MVC), zalecane jest ASP.NET Core Identity.

Uwierzytelnianie za pomocą dostawców zewnętrznych

ASP.NET Core obsługuje również używanie zewnętrznych dostawców uwierzytelniania w celu zezwalania użytkownikom na logowanie za pośrednictwem przepływów protokołu OAuth 2.0 . Oznacza to, że użytkownicy mogą logować się przy użyciu istniejących procesów uwierzytelniania od dostawców, takich jak Microsoft, Google, Facebook lub Twitter, i skojarzyć te tożsamości z tożsamością ASP.NET Core w aplikacji.

Aby użyć uwierzytelniania zewnętrznego, oprócz oprogramowania pośredniczącego uwierzytelniania, jak wspomniano wcześniej, przy użyciu metody app.UseAuthentication(), należy również zarejestrować dostawcę zewnętrznego w Program.cs, jak pokazano w poniższym przykładzie:

//...
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

services.AddAuthentication()
    .AddMicrosoftAccount(microsoftOptions =>
    {
        microsoftOptions.ClientId = builder.Configuration["Authentication:Microsoft:ClientId"];
        microsoftOptions.ClientSecret = builder.Configuration["Authentication:Microsoft:ClientSecret"];
    })
    .AddGoogle(googleOptions => { ... })
    .AddTwitter(twitterOptions => { ... })
    .AddFacebook(facebookOptions => { ... });
//...

W poniższej tabeli przedstawiono popularnych dostawców uwierzytelniania zewnętrznego i skojarzonych z nimi pakietów NuGet:

Dostawca Pakiet
Microsoft Microsoft.AspNetCore.Authentication.MicrosoftAccount
Google Microsoft.AspNetCore.Authentication.Google
Facebook Microsoft.AspNetCore.Authentication.Facebook
Twitter Microsoft.AspNetCore.Authentication.Twitter

We wszystkich przypadkach należy wykonać procedurę rejestracji aplikacji zależną od dostawcy, która zwykle obejmuje:

  1. Pobieranie identyfikatora aplikacji klienckiej.
  2. Uzyskiwanie sekretu aplikacji klienckiej.
  3. Konfigurowanie adresu URL przekierowania obsługiwanego przez oprogramowanie pośredniczące autoryzacji i zarejestrowanego dostawcę
  4. Opcjonalnie skonfigurowanie adresu URL wylogowywania w celu prawidłowego obsługi wylogowania w scenariuszu logowania jednokrotnego.

Aby uzyskać szczegółowe informacje na temat konfigurowania aplikacji dla dostawcy zewnętrznego, zobacz uwierzytelnianie dostawcy zewnętrznego w dokumentacji platformy ASP.NET Core).

Wskazówka

Wszystkie szczegóły są obsługiwane przez wcześniej wymienione oprogramowanie pośredniczące autoryzacji i usługi. Dlatego po prostu musisz wybrać opcję uwierzytelniania indywidualnego konta użytkownika podczas tworzenia projektu aplikacji internetowej ASP.NET Core w programie Visual Studio, jak pokazano na rysunku 9-3, oprócz zarejestrowania wcześniej wymienionych dostawców uwierzytelniania.

Zrzut ekranu przedstawiający okno dialogowe Nowa aplikacja internetowa ASP.NET Core.

Rysunek 9–3. Wybranie opcji Indywidualne konta użytkowników na potrzeby korzystania z uwierzytelniania zewnętrznego podczas tworzenia projektu aplikacji internetowej w programie Visual Studio 2019.

Oprócz wymienionych wcześniej zewnętrznych dostawców uwierzytelniania dostępne są pakiety innych firm, które zapewniają oprogramowanie pośredniczące do korzystania z wielu innych dostawców uwierzytelniania zewnętrznego. Aby uzyskać listę, zobacz repozytorium AspNet.Security.OAuth.Providers w witrynie GitHub.

Możesz również utworzyć własne oprogramowanie pośredniczące uwierzytelniania zewnętrznego, aby rozwiązać pewne szczególne potrzeby.

Uwierzytelnianie za pomocą tokenów elementu nośnego

Uwierzytelnianie za pomocą ASP.NET Core Identity (lub Identity plus zewnętrzni dostawcy uwierzytelniania) działa dobrze w przypadku wielu scenariuszy aplikacji internetowych, w których przechowywanie informacji o użytkowniku w pliku cookie jest odpowiednie. W innych scenariuszach pliki cookie nie są jednak naturalnym sposobem utrwalania i przesyłania danych.

Na przykład w ASP.NET Core Web API, które udostępnia endpointy RESTowe, wykorzystywane przez aplikacje jednostronicowe (SPA), klientów natywnych czy nawet inne interfejsy Web API, często lepiej jest użyć uwierzytelniania tokenami typu bearer. Tego typu aplikacje nie działają z plikami cookie, ale można łatwo pobrać token nosiciela i dołączyć go do nagłówka autoryzacji kolejnych żądań. Aby włączyć uwierzytelnianie tokenów, ASP.NET Core obsługuje kilka opcji używania protokołu OAuth 2.0 i OpenID Connect.

Uwierzytelnij się za pomocą dostawcy tożsamości OpenID Connect lub OAuth 2.0

Jeśli informacje o użytkowniku są przechowywane w usłudze Azure Active Directory lub innym rozwiązaniu tożsamości obsługującym protokół OpenID Connect lub OAuth 2.0, możesz użyć pakietu Microsoft.AspNetCore.Authentication.OpenIdConnect do uwierzytelniania przy użyciu przepływu pracy OpenID Connect. Na przykład aby uwierzytelnić się w mikrousłudze Identity.Api w aplikacji eShopOnContainers, aplikacja internetowa ASP.NET Core może używać oprogramowania pośredniczącego z tego pakietu, jak pokazano w poniższym uproszczonym przykładzie w Program.cs:

// Program.cs

var identityUrl = builder.Configuration.GetValue<string>("IdentityUrl");
var callBackUrl = builder.Configuration.GetValue<string>("CallBackUrl");
var sessionCookieLifetime = builder.Configuration.GetValue("SessionCookieLifetimeMinutes", 60);

// Add Authentication services

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime))
.AddOpenIdConnect(options =>
{
    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.Authority = identityUrl.ToString();
    options.SignedOutRedirectUri = callBackUrl.ToString();
    options.ClientId = useLoadTest ? "mvctest" : "mvc";
    options.ClientSecret = "secret";
    options.ResponseType = useLoadTest ? "code id_token token" : "code id_token";
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;
    options.RequireHttpsMetadata = false;
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("orders");
    options.Scope.Add("basket");
    options.Scope.Add("marketing");
    options.Scope.Add("locations");
    options.Scope.Add("webshoppingagg");
    options.Scope.Add("orders.signalrhub");
});

// Build the app
//…
app.UseAuthentication();
//…
app.UseEndpoints(endpoints =>
{
    //...
});

W przypadku korzystania z tego przepływu pracy oprogramowanie pośredniczące ASP.NET Core Identity nie jest potrzebne, ponieważ cały magazyn informacji o użytkowniku i uwierzytelnianie są obsługiwane przez usługę Identity.

Wystawianie tokenów zabezpieczających z usługi ASP.NET Core

Jeśli wolisz wystawiać tokeny zabezpieczające dla lokalnych użytkowników ASP.NET Core Identity zamiast korzystać z zewnętrznego dostawcy tożsamości, możesz skorzystać z niektórych dobrych bibliotek innych firm.

IdentityServer4 i OpenIddict to dostawcy openID Connect, którzy łatwo integrują się z tożsamością ASP.NET Core, aby umożliwić wystawianie tokenów zabezpieczających z usługi ASP.NET Core. Dokumentacja identityServer4 zawiera szczegółowe instrukcje dotyczące korzystania z biblioteki. Jednak podstawowe kroki używania maszyny wirtualnej IdentityServer4 do wystawiania tokenów są następujące.

  1. Serwer IdentityServer4 można skonfigurować w Program.cs , wykonując wywołanie konstruktora. Services.AddIdentityServer.

  2. Wywołujesz UseIdentityServer w Program.cs, aby dodać IdentityServer4 do rurociągu przetwarzania żądań HTTP aplikacji. Dzięki temu biblioteka może obsługiwać żądania do punktów końcowych OpenID Connect i OAuth2, takich jak /connect/token.

  3. Serwer tożsamości można skonfigurować, ustawiając następujące dane:

    • Poświadczenia do użycia do podpisywania.

    • Zasoby tożsamości i interfejsu API, do których użytkownicy mogą żądać dostępu:

      • Zasoby interfejsu API reprezentują chronione dane lub funkcje, do których użytkownik może uzyskać dostęp za pomocą tokenu dostępu. Przykładem zasobu interfejsu API jest web API (lub zestaw interfejsów API), który wymaga autoryzacji.

      • Zasoby tożsamości reprezentują informacje (oświadczenia), które są przekazywane klientowi w celu zidentyfikowania użytkownika. Oświadczenia mogą zawierać nazwę użytkownika, adres e-mail itd.

    • Klienci, którzy będą łączyć się w celu żądania tokenów.

    • Mechanizm przechowywania informacji o użytkowniku, taki jak ASP.NET Tożsamość podstawowa lub alternatywa.

Po określeniu klientów i zasobów, których ma używać IdentityServer4, można przekazać kolekcję IEnumerable<T> odpowiedniego typu do metod, które przyjmują przechowywanie klientów lub zasobów w pamięci. W przypadku bardziej złożonych scenariuszy można podać typy klientów lub dostawców zasobów poprzez mechanizm wstrzykiwania zależności.

Przykładowa konfiguracja IdentityServer4 do używania zasobów w pamięci i klientów obsługiwanych przez niestandardowy typ IClientStore może wyglądać podobnie do następującego przykładu:

// Program.cs

builder.Services.AddSingleton<IClientStore, CustomClientStore>();
builder.Services.AddIdentityServer()
    .AddSigningCredential("CN=sts")
    .AddInMemoryApiResources(MyApiResourceProvider.GetAllResources())
    .AddAspNetIdentity<ApplicationUser>();
//...

Zużywanie tokenów zabezpieczających

Uwierzytelnianie względem punktu końcowego OpenID Connect lub wystawianie własnych tokenów bezpieczeństwa obejmuje niektóre scenariusze. Ale co z usługą, która po prostu musi ograniczyć dostęp do tych użytkowników, którzy mają prawidłowe tokeny zabezpieczające, które zostały dostarczone przez inną usługę?

W tym scenariuszu oprogramowanie pośredniczące uwierzytelniania obsługujące tokeny JWT jest dostępne w pakiecie Microsoft.AspNetCore.Authentication.JwtBearer . JWT oznacza "token internetowy JSON" i jest typowym formatem tokenu zabezpieczającego (zdefiniowanym przez RFC 7519) do komunikowania oświadczeń zabezpieczeń. Uproszczony przykład używania oprogramowania pośredniczącego do korzystania z takich tokenów może wyglądać podobnie do tego fragmentu kodu pobranego z mikrousługi Ordering.Api eShopOnContainers.

// Program.cs

var identityUrl = builder.Configuration.GetValue<string>("IdentityUrl");

// Add Authentication services

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(options =>
{
    options.Authority = identityUrl;
    options.RequireHttpsMetadata = false;
    options.Audience = "orders";
});

// Build the app

app.UseAuthentication();
//…
app.UseEndpoints(endpoints =>
{
    //...
});

Parametry w tym użyciu to:

  • Audience reprezentuje odbiornik tokenu przychodzącego lub zasobu, do którego token udziela dostępu. Jeśli wartość określona w tym parametrze jest niezgodna z parametrem w tokenie, token zostanie odrzucony.

  • Authority to adres serwera uwierzytelniania wystawiającego tokeny. Oprogramowanie pośredniczące uwierzytelniania elementu nośnego JWT używa tego identyfikatora URI, aby uzyskać klucz publiczny, który może służyć do sprawdzania poprawności podpisu tokenu. Oprogramowanie pośredniczące potwierdza również, że parametr tokena iss jest zgodny z tym identyfikatorem URI.

Inny parametr , RequireHttpsMetadatajest przydatny do celów testowych. Ten parametr ma wartość false, aby można było testować w środowiskach, w których nie masz certyfikatów. W rzeczywistych wdrożeniach tokeny elementu nośnego JWT powinny być zawsze przekazywane tylko za pośrednictwem protokołu HTTPS.

W przypadku tego oprogramowania pośredniczącego tokeny JWT są automatycznie wyodrębniane z nagłówków autoryzacji. Następnie są deserializowane, weryfikowane (przy użyciu wartości w Audience parametrach i Authority ) i przechowywane jako informacje o użytkowniku, które mają być przywoływały później przez akcje MVC lub filtry autoryzacji.

Warstwa pośrednicząca uwierzytelniania JWT może również obsługiwać bardziej zaawansowane scenariusze, takie jak użycie certyfikatu lokalnego do weryfikacji tokenu, jeśli brak jest dostępu do instancji autoryzacyjnej. W tym scenariuszu można określić obiekt TokenValidationParameters w obiekcie JwtBearerOptions.

Dodatkowe zasoby