Udostępnij za pośrednictwem


Jak użyć Identity do zabezpieczenia zaplecza internetowego interfejsu API dla SPA

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję artykułu dla .NET 9.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz wersję artykułu dla .NET 9.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zobacz wersję artykułu dla .NET 9.

ASP.NET Core Identity udostępnia interfejsy API obsługujące uwierzytelnianie, autoryzację i zarządzanie tożsamościami. Interfejsy API umożliwiają zabezpieczanie punktów końcowych zaplecza interfejsu internetowego API przy użyciu uwierzytelniania opartego na cookie. Opcja oparta na tokenach jest dostępna dla klientów, którzy nie mogą używać plików cookie, ale w tym celu ponosisz odpowiedzialność za zapewnienie bezpieczeństwa tokenów. Zalecamy używanie plików cookie dla aplikacji opartych na przeglądarce, ponieważ domyślnie przeglądarka automatycznie obsługuje je bez uwidaczniania ich w języku JavaScript.

W tym artykule pokazano, jak używać Identity do zabezpieczania zaplecza internetowego interfejsu API dla aplikacji typu SPA, takich jak Angular, React i Vue. Te same interfejsy API zaplecza mogą służyć do zabezpieczania Blazor WebAssembly aplikacji.

Wymagania wstępne

W tym artykule przedstawiono kroki, które umożliwiają dodanie uwierzytelniania i autoryzacji dla aplikacji ASP.NET Core Web API, która:

  • Nie jest jeszcze skonfigurowany do uwierzytelniania.
  • Obiekty docelowe net8.0 lub nowsze.
  • Może to być API minimalne lub oparte na kontrolerze.

Niektóre instrukcje testowe w tym artykule korzystają z Swagger UI zawartego w szablonie projektu. Nie jest wymagany interfejs użytkownika Swagger do użycia Identity z backendem Web API.

Instalowanie pakietów NuGet

Zainstaluj następujące pakiety NuGet:

Aby uzyskać najszybszy sposób rozpoczęcia pracy, użyj bazy danych w pamięci.

Zmień bazę danych później na SQLite lub SQL Server, aby zapisać dane użytkownika między sesjami podczas testowania lub użycia produkcyjnego. Wprowadza to pewną złożoność w porównaniu z pamięcią w RAM, ponieważ wymaga utworzenia bazy danych za pośrednictwem migracji, jak pokazano w samouczku EF Core dla początkujących.

Zainstaluj te pakiety przy użyciu menedżera pakietów NuGet w programie Visual Studio lub polecenia dotnet add package CLI.

Utwórz IdentityDbContext

Dodaj klasę o nazwie ApplicationDbContext , która dziedziczy z klasy IdentityDbContext<TUser>:

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) :
        base(options)
    { }
}

Pokazany kod udostępnia specjalny konstruktor, który umożliwia skonfigurowanie bazy danych dla różnych środowisk.

Dodaj jedną lub więcej z poniższych dyrektyw using zgodnie z potrzebami podczas dodawania kodu pokazanego w poniższych krokach.

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

Skonfiguruj EF Core kontekst

Jak wspomniano wcześniej, najprostszym sposobem rozpoczęcia pracy jest użycie bazy danych w pamięci. W przypadku uruchamiania w pamięci każde uruchomienie rozpoczyna się od nowej bazy danych i nie ma potrzeby używania migracji. Po wywołaniu metody WebApplication.CreateBuilder(args), dodaj następujący kod, aby skonfigurować Identity do używania bazy danych w pamięci:

builder.Services.AddDbContext<ApplicationDbContext>(
    options => options.UseInMemoryDatabase("AppDb"));

Aby zapisać dane użytkownika między sesjami podczas testowania lub użycia produkcyjnego, zmień bazę danych później na SQLite lub SQL Server.

Dodawanie Identity usług do kontenera

Po wywołaniu WebApplication.CreateBuilder(args) wywołaj AddAuthorization, aby dodać usługi do kontenera wstrzykiwania zależności (DI).

builder.Services.AddAuthorization();

Aktywuj Identity interfejsy API

Po wywołaniu WebApplication.CreateBuilder(args), wywołaj AddIdentityApiEndpoints<TUser>(IServiceCollection) i AddEntityFrameworkStores<TContext>(IdentityBuilder).

builder.Services.AddIdentityApiEndpoints<IdentityUser>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

Domyślnie zarówno pliki cookie, jak i zastrzeżone tokeny są aktywowane. Jeśli w punkcie końcowym logowania parametr ciągu zapytania useCookies jest true, pliki cookie i tokeny są wystawiane podczas logowania.

Mapuj Identity trasy

Po wywołaniu builder.Build(), wywołaj MapIdentityApi<TUser>(IEndpointRouteBuilder), aby zmapować punkty końcowe Identity.

app.MapIdentityApi<IdentityUser>();

Zabezpieczanie wybranych punktów końcowych

Aby zabezpieczyć punkt końcowy, użyj RequireAuthorization metody rozszerzenia w wywołaniu Map{Method} definiującym trasę. Na przykład:

app.MapGet("/weatherforecast", (HttpContext httpContext) =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = summaries[Random.Shared.Next(summaries.Length)]
        })
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi()
.RequireAuthorization();

Metody RequireAuthorization można również użyć do:

  • Zabezpieczanie endpointów interfejsu użytkownika Swagger, jak pokazano w poniższym przykładzie:

    app.MapSwagger().RequireAuthorization();
    
  • Zabezpiecz się przy użyciu określonego oświadczenia lub uprawnienia, jak pokazano w poniższym przykładzie:

    .RequireAuthorization("Admin");
    

W projekcie internetowego interfejsu API opartego na kontrolerze zabezpiecz punkty końcowe, stosując atrybut [Authorize] do kontrolera lub akcji.

Testowanie interfejsu API

Szybkim sposobem testowania uwierzytelniania jest użycie bazy danych w pamięci i interfejsu użytkownika programu Swagger dołączonego do szablonu projektu. W poniższych krokach pokazano, jak przetestować API za pomocą Swagger UI. Upewnij się, że punkty końcowe Swagger UI nie są zabezpieczone.

Próba uzyskania dostępu do zabezpieczonego punktu końcowego

  • Uruchom aplikację i przejdź do interfejsu użytkownika programu Swagger.
  • Rozwiń zabezpieczony punkt końcowy, taki jak /weatherforecast w projekcie utworzonym z szablonu web API.
  • Wybierz pozycję Wypróbuj.
  • Wybierz polecenie Wykonaj. Odpowiedź to 401 - not authorized.

Rejestracja na test

  • Rozwiń /register i wybierz pozycję Wypróbuj.

  • W sekcji Parametry interfejsu użytkownika jest wyświetlana przykładowa treść żądania:

    {
      "email": "string",
      "password": "string"
    }
    
  • Zastąp ciąg prawidłowym adresem e-mail i hasłem, a następnie wybierz pozycję Wykonaj.

    Aby zachować zgodność z domyślnymi regułami sprawdzania poprawności haseł, hasło musi mieć długość co najmniej sześciu znaków i zawierać co najmniej jeden z następujących znaków:

    • Wielka litera
    • Mała litera
    • Cyfra liczbowa
    • Znak niealfanumeryczny

    Jeśli wprowadzisz nieprawidłowy adres e-mail lub nieprawidłowe hasło, wynik zawiera błędy walidacji. Oto przykład treści odpowiedzi z błędami walidacji:

    {
      "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
      "title": "One or more validation errors occurred.",
      "status": 400,
      "errors": {
        "PasswordTooShort": [
          "Passwords must be at least 6 characters."
        ],
        "PasswordRequiresNonAlphanumeric": [
          "Passwords must have at least one non alphanumeric character."
        ],
        "PasswordRequiresDigit": [
          "Passwords must have at least one digit ('0'-'9')."
        ],
        "PasswordRequiresLower": [
          "Passwords must have at least one lowercase ('a'-'z')."
        ]
      }
    }
    

    Błędy są zwracane w formacie ProblemDetails , aby klient mógł je analizować i wyświetlać błędy walidacji zgodnie z potrzebami.

    Pomyślna rejestracja powoduje 200 - OK odpowiedź.

Testowanie logowania

  • Rozwiń /login i wybierz pozycję Wypróbuj. Przykładowa treść żądania zawiera dwa dodatkowe parametry:

    {
      "email": "string",
      "password": "string",
      "twoFactorCode": "string",
      "twoFactorRecoveryCode": "string"
    }
    

    Dodatkowe właściwości JSON nie są potrzebne w tym przykładzie i można je usunąć. Ustaw wartość opcji useCookies na true.

  • Zastąp ciąg adresem e-mail i hasłem, których użyłeś podczas rejestracji, a następnie wybierz pozycję Wykonaj.

    Pomyślne zalogowanie powoduje otrzymanie 200 - OK odpowiedzi z nagłówkiem odpowiedzi cookie.

Ponowne testowanie zabezpieczonego punktu końcowego

Po pomyślnym zalogowaniu uruchom ponownie zabezpieczony punkt końcowy. Uwierzytelnianie cookie jest wysyłane automatycznie z żądaniem, a punkt końcowy jest autoryzowany. Cookie-oparte uwierzytelnianie jest bezpiecznie wbudowane w przeglądarkę i działa bez problemu.

Testowanie z użyciem klientów nieprzeglądarkowych

Niektórzy klienci sieci Web mogą domyślnie nie dołączać plików cookie do nagłówka:

  • Jeśli używasz narzędzia do testowania interfejsów API, może być konieczne włączenie plików cookie w ustawieniach.

  • Interfejs API języka JavaScript fetch domyślnie nie zawiera plików cookie. Włącz je, ustawiając w opcjach credentials na wartość include.

  • Uruchomienie HttpClient aplikacji Blazor WebAssembly wymaga HttpRequestMessage uwzględnienia poświadczeń, takich jak w poniższym przykładzie:

    request.SetBrowserRequestCredential(BrowserRequestCredentials.Include);
    

Korzystanie z uwierzytelniania opartego na tokenach

Zalecamy używanie plików cookie w aplikacjach opartych na przeglądarce, ponieważ domyślnie przeglądarka automatycznie obsługuje je bez ujawniania ich w języku JavaScript.

Wystawiono token niestandardowy (zastrzeżony dla platformy tożsamości ASP.NET Core), który może służyć do uwierzytelniania kolejnych żądań. Token jest przekazywany w nagłówku Authorization jako token elementu nośnego. Dostarczany jest również token odświeżania. Ten token umożliwia aplikacji żądanie nowego tokenu po wygaśnięciu starego tokenu bez wymuszania ponownego zalogowania się użytkownika.

Tokeny nie są standardowymi tokenami internetowymi JSON (JWTs). Użycie tokenów niestandardowych jest zamierzone, ponieważ wbudowany Identity interfejs API jest przeznaczony głównie dla prostych scenariuszy. Opcja tokenu nie ma być w pełni funkcjonalnym dostawcą usług tożsamości lub serwerem tokenów, ale zamiast tego alternatywą dla cookie opcji dla klientów, którzy nie mogą używać plików cookie.

Aby użyć uwierzytelniania opartego na tokenach, ustaw parametr ciągu zapytania useCookies na false podczas wywoływania punktu końcowego /login. Tokeny używają schematu uwierzytelniania bearer. Użycie tokenu zwróconego z wywołania do /login, kolejne wywołania chronionych punktów końcowych powinny dodać nagłówek Authorization: Bearer <token>, w którym <token> znajduje się token dostępu. Aby uzyskać więcej informacji, zobacz Używanie punktu końcowego POST /login w dalszej części tego artykułu.

Wyloguj się

Aby umożliwić użytkownikowi wylogowanie się, zdefiniuj /logout punkt końcowy podobny do następującego przykładu:

app.MapPost("/logout", async (SignInManager<IdentityUser> signInManager,
    [FromBody] object empty) =>
{
    if (empty != null)
    {
        await signInManager.SignOutAsync();
        return Results.Ok();
    }
    return Results.Unauthorized();
})
.WithOpenApi()
.RequireAuthorization();

Podaj pusty obiekt JSON ({}) w treści żądania podczas wywoływania tego punktu końcowego. Poniższy kod to przykład wywołania punktu końcowego wylogowania:

public signOut() {
  return this.http.post('/logout', {}, {
    withCredentials: true,
    observe: 'response',
    responseType: 'text'

Punkty MapIdentityApi<TUser> końcowe

Wywołanie MapIdentityApi<TUser> dodaje następujące punkty końcowe do aplikacji:

Użyj punktu końcowego POST /register

Treść żądania musi zawierać właściwości Email i Password.

{
  "email": "string",
  "password": "string",
}

Aby uzyskać więcej informacji, zobacz:

Użyj punktu końcowego POST /login

W treści żądania, Email i Password są wymagane. Jeśli włączono uwierzytelnianie dwuskładnikowe (2FA), wymagane jest albo TwoFactorCode, albo TwoFactorRecoveryCode. Jeśli 2FA nie jest włączone, pomiń zarówno twoFactorCode, jak i twoFactorRecoveryCode. Aby uzyskać więcej informacji, zobacz Używanie punktu końcowego POST /manage/2fa w dalszej części tego artykułu.

Oto przykład treści żądania z wyłączonym uwierzytelnianiem dwuetapowym 2FA:

{
  "email": "string",
  "password": "string"
}

Oto przykłady treści żądań z włączoną usługą 2FA:

  • {
      "email": "string",
      "password": "string",
      "twoFactorCode": "string",
    }
    
  • {
      "email": "string",
      "password": "string",
      "twoFactorRecoveryCode": "string"
    }
    

Punkt końcowy oczekuje parametru ciągu zapytania:

  • useCookies - Ustaw na true wartość dla uwierzytelniania opartego na cookie. Ustaw na false lub pomiń dla uwierzytelniania opartego na tokenach.

Aby uzyskać więcej informacji na temat uwierzytelniania opartego na cookieprotokole, zobacz Testowanie logowania we wcześniejszej sekcji tego artykułu.

Uwierzytelnianie oparte na tokenach

Jeśli useCookies pasuje do false lub zostanie pominięte, jest włączone uwierzytelnianie oparte na tokenach. Treść odpowiedzi zawiera następujące właściwości:

{
  "tokenType": "string",
  "accessToken": "string",
  "expiresIn": 0,
  "refreshToken": "string"
}

Aby uzyskać więcej informacji na temat tych właściwości, zobacz AccessTokenResponse.

Umieść token dostępu w nagłówku, aby wysyłać uwierzytelnione żądania, jak pokazano w poniższym przykładzie

Authorization: Bearer {access token}

Gdy ten token dostępu ma wkrótce wygasnąć, wywołaj interfejs /refresh.

Użyj punktu końcowego POST /refresh

Do użycia tylko w przypadku uwierzytelniania opartego na tokenach. Pobiera nowy token dostępu bez wymuszania ponownego logowania użytkownika. Wywołaj ten punkt końcowy, gdy token dostępu wkrótce wygaśnie.

Treść żądania zawiera tylko element RefreshToken. Oto przykład treści żądania:

{
  "refreshToken": "string"
}

Jeśli wywołanie zakończy się pomyślnie, treść odpowiedzi to nowy AccessTokenResponseelement, jak pokazano w poniższym przykładzie.

{
  "tokenType": "string",
  "accessToken": "string",
  "expiresIn": 0,
  "refreshToken": "string"
}

Użyj punktu końcowego GET /confirmEmail

Jeśli Identity jest skonfigurowany do potwierdzenia wiadomości e-mail, pomyślne wywołanie punktu końcowego /register spowoduje wysłanie wiadomości e-mail zawierającej link do punktu końcowego /confirmEmail. Link zawiera następujące parametry ciągu zapytania:

  • userId
  • code
  • changedEmail - Uwzględnione tylko wtedy, gdy użytkownik zmienił adres e-mail podczas rejestracji.

Identity Zawiera domyślny tekst wiadomości e-mail z potwierdzeniem. Domyślnie temat wiadomości e-mail to "Potwierdź wiadomość e-mail", a treść wiadomości e-mail wygląda następująco:

 Please confirm your account by <a href='https://contoso.com/confirmEmail?userId={user ID}&code={generated code}&changedEmail={new email address}'>clicking here</a>.

RequireConfirmedEmail Jeśli właściwość jest ustawiona na true, użytkownik nie może się zalogować, dopóki adres e-mail nie zostanie potwierdzony, klikając link w wiadomości e-mail. Punkt /confirmEmail końcowy:

  • Potwierdza adres e-mail i umożliwia użytkownikowi logowanie się.
  • Zwraca tekst "Dziękujemy za potwierdzenie wiadomości e-mail" w treści odpowiedzi.

Aby skonfigurować Identity do potwierdzania wiadomości e-mail, dodaj kod w Program.cs, aby ustawić RequireConfirmedEmail na true i dodaj klasę, która implementuje IEmailSender do kontenera Iniekcji Zależności. Na przykład:

builder.Services.Configure<IdentityOptions>(options =>
{
    options.SignIn.RequireConfirmedEmail = true;
});

builder.Services.AddTransient<IEmailSender, EmailSender>();

Aby uzyskać więcej informacji, zobacz Potwierdzanie konta i odzyskiwanie hasła w usłudze ASP.NET Core.

Identity Udostępnia domyślny tekst dla innych wiadomości e-mail, które należy również wysłać, na przykład w przypadku uwierzytelniania 2FA i resetowania hasła. Aby dostosować te wiadomości e-mail, podaj niestandardową implementację interfejsu IEmailSender . W poprzednim przykładzie EmailSender jest klasą, która implementuje IEmailSender. Aby uzyskać więcej informacji, w tym przykład klasy, która implementuje IEmailSender, zobacz Potwierdzanie konta i odzyskiwanie hasła w ASP.NET Core.

Użyj punktu końcowego POST /resendConfirmationEmail

Wysyła wiadomość e-mail tylko wtedy, gdy adres jest prawidłowy dla zarejestrowanego użytkownika.

Treść żądania zawiera tylko element Email. Oto przykład treści żądania:

{
  "email": "string"
}

Aby uzyskać więcej informacji, zobacz sekcję Używanie punktu końcowego GET /confirmEmail wcześniej w tym artykule.

Użyj punktu końcowego POST /forgotPassword

Generuje wiadomość e-mail zawierającą kod resetowania hasła. Wyślij ten kod na /resetPassword adres przy użyciu nowego hasła.

Treść żądania zawiera tylko element Email. Oto przykład:

{
  "email": "string"
}

Aby uzyskać informacje o tym, jak włączyć Identity do wysyłania wiadomości e-mail, zobacz Jak korzystać z punktu końcowegoGET /confirmEmail.

Użyj punktu końcowego POST /resetPassword

Wywołaj ten punkt końcowy po wywołaniu punktu końcowego /forgotPassword i otrzymaniu kodu resetowania.

Treść żądania wymaga Email, ResetCode oraz NewPassword. Oto przykład:

{
  "email": "string",
  "resetCode": "string",
  "newPassword": "string"
}

Użyj punktu końcowego POST /manage/2fa

Konfiguruje uwierzytelnianie dwuskładnikowe (2FA) dla użytkownika. Po włączeniu uwierzytelniania 2FA, pomyślne logowanie wymaga kodu wygenerowanego przez aplikację uwierzytelniającą, oprócz adresu e-mail i hasła.

Włącz 2FA

Aby włączyć uwierzytelnianie 2FA dla aktualnie uwierzytelnioowanego użytkownika:

  • Wywołaj endpoint /manage/2fa, przesyłając pusty obiekt JSON ({}) w treści żądania.

  • Treść odpowiedzi udostępnia SharedKey wraz z innymi właściwościami, które nie są obecnie potrzebne. Klucz wspólny służy do konfigurowania aplikacji authenticator. Przykład treści odpowiedzi:

    {
      "sharedKey": "string",
      "recoveryCodesLeft": 0,
      "recoveryCodes": null,
      "isTwoFactorEnabled": false,
      "isMachineRemembered": false
    }
    
  • Użyj klucza współużytkowanego, aby uzyskać jednorazowe hasło oparte na czasie (TOTP). Aby uzyskać więcej informacji, zobacz Enable QR code generation for TOTP authenticator apps in ASP.NET Core (Włączanie generowania kodu QR dla aplikacji uwierzytelniania TOTP w usłudze ASP.NET Core).

  • Wywołaj punkt dostępu /manage/2fa, wysyłając kod TOTP i "enable": true w treści żądania. Na przykład:

    {
      "enable": true,
      "twoFactorCode": "string"
    }
    
  • Treść odpowiedzi potwierdza, że IsTwoFactorEnabled ma wartość true i udostępnia element RecoveryCodes. Kody odzyskiwania są używane do logowania się, gdy aplikacja uwierzytelniająca nie jest dostępna. Przykład treści odpowiedzi po pomyślnym włączeniu uwierzytelniania 2FA:

    {
      "sharedKey": "string",
      "recoveryCodesLeft": 10,
      "recoveryCodes": [
        "string",
        "string",
        "string",
        "string",
        "string",
        "string",
        "string",
        "string",
        "string",
        "string"
      ],
      "isTwoFactorEnabled": true,
      "isMachineRemembered": false
    }
    

Logowanie przy użyciu uwierzytelniania 2FA

Wywołaj punkt końcowy /login, przesyłając adres e-mail, hasło i TOTP w treści żądania. Na przykład:

{
  "email": "string",
  "password": "string",
  "twoFactorCode": "string"
}

Jeśli użytkownik nie ma dostępu do aplikacji uwierzytelniającej, zaloguj się, korzystając z punktu końcowego /login przy użyciu jednego z kodów odzyskiwania dostarczonych w momencie aktywacji 2FA. Treść żądania wygląda podobnie do następującego przykładu:

{
  "email": "string",
  "password": "string",
  "twoFactorRecoveryCode": "string"
}

Resetowanie kodów odzyskiwania

Aby uzyskać nową kolekcję kodów odzyskiwania, wywołaj ten punkt końcowy z ustawieniem ResetRecoveryCodes na true. Oto przykład treści żądania:

{
  "resetRecoveryCodes": true
}

Resetowanie klucza współużytkowanego

Aby uzyskać nowy losowy klucz współużytkowany, skorzystaj z tego punktu końcowego, ustawiając ResetSharedKey na true. Oto przykład treści żądania:

{
  "resetSharedKey": true
}

Zresetowanie klucza powoduje automatyczne wyłączenie wymagania logowania dwuskładnikowego dla uwierzytelnionego użytkownika do momentu ponownego włączenia go przez późniejsze żądanie.

Zapomnij o maszynie

Aby usunąć flagę cookie "zapamiętaj mnie", jeśli jest obecna, wywołaj ten endpoint z wartością ForgetMachine ustawioną na true. Oto przykład treści żądania:

{
  "forgetMachine": true
}

Ten punkt końcowy nie ma wpływu na uwierzytelnianie oparte na tokenach.

Użyj punktu końcowego GET /manage/info

Pobiera adres e-mail i stan potwierdzenia wiadomości e-mail zalogowanego użytkownika. Oświadczenia zostały pominięte w tym punkcie końcowym ze względów bezpieczeństwa. Jeśli są potrzebne oświadczenia, użyj interfejsów API po stronie serwera, aby skonfigurować punkt końcowy dla oświadczeń. Lub zamiast udostępniać wszystkie oświadczenia użytkowników, podaj punkt końcowy weryfikacji, który akceptuje oświadczenie i odpowiada, czy użytkownik ma je.

Żądanie nie wymaga żadnych parametrów. Treść odpowiedzi zawiera właściwości Email i IsEmailConfirmed, jak w poniższym przykładzie.

{
  "email": "string",
  "isEmailConfirmed": true
}

Użyj punktu końcowego POST /manage/info

Aktualizuje adres e-mail i hasło zalogowanego użytkownika. Wyślij NewEmail, NewPassword i OldPassword w treści żądania, jak pokazano w poniższym przykładzie:

{
  "newEmail": "string",
  "newPassword": "string",
  "oldPassword": "string"
}

Oto przykład treści odpowiedzi:

{
  "email": "string",
  "isEmailConfirmed": false
}

Zobacz też

Aby uzyskać więcej informacji, zobacz następujące zasoby:

Szablony ASP.NET Core oferują uwierzytelnianie w aplikacjach jednostronicowych (SPA) przy użyciu obsługi autoryzacji interfejsu API. ASP.NET Core Identity na potrzeby uwierzytelniania i przechowywania użytkowników jest połączony z serwerem Duende Identity do implementowania programu OpenID Connect.

Ważne

Oprogramowanie Duende może wymagać zapłacenia opłaty licencyjnej za korzystanie z serwera Duende Identity Server w środowisku produkcyjnym. Aby uzyskać więcej informacji, zobacz Migracja z platformy ASP.NET Core w wersji 5.0 do wersji 6.0.

Parametr uwierzytelniania został dodany do szablonów projektów Angular i React, podobny do parametru uwierzytelniania w szablonach projektów aplikacji internetowej (Model-View-Controller) oraz aplikacji internetowej (Razor Pages). Dozwolone wartości parametrów to None i Individual. Szablon projektu React.js i Redux nie obsługuje obecnie parametru uwierzytelniania.

Tworzenie aplikacji z obsługą autoryzacji interfejsu API

Uwierzytelnianie i autoryzacja użytkownika mogą być używane zarówno z usługami Angular, jak i React SPA. Otwórz konsolę i uruchom następujące polecenie:

Angular:

dotnet new angular -au Individual

React:

dotnet new react -au Individual

Poprzednie polecenie tworzy aplikację ASP.NET Core z katalogiem ClientApp zawierającym SPA.

Ogólny opis składników ASP.NET Core aplikacji

W poniższych sekcjach opisano dodatki do projektu, gdy jest uwzględniona obsługa uwierzytelniania:

Program.cs

Poniższe przykłady kodu bazują na pakiecie NuGet Microsoft.AspNetCore.ApiAuthorization.IdentityServer . Przykłady konfigurują uwierzytelnianie i autoryzację API przy użyciu metod rozszerzeń AddApiAuthorization i AddIdentityServerJwt. Projekty korzystające z szablonów projektów React lub Angular SPA z uwierzytelnianiem obejmują odwołanie do tego pakietu.

dotnet new angular -au Individual generuje następujący Program.cs plik:

using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using output_directory_name.Data;
using output_directory_name.Models;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

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

builder.Services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

builder.Services.AddAuthentication()
    .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

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

app.MapControllerRoute(
    name: "default",
    pattern: "{controller}/{action=Index}/{id?}");
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

Powyższy kod konfiguruje:

  • Identity z domyślnym interfejsem użytkownika:

    builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlite(connectionString));
    builder.Services.AddDatabaseDeveloperPageExceptionFilter();
    
    builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    
  • IdentityServer z dodatkową AddApiAuthorization metodą pomocnika, która konfiguruje niektóre domyślne konwencje ASP.NET Core na serwerze IdentityServer:

    builder.Services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
    
  • Uwierzytelnianie przy użyciu dodatkowej AddIdentityServerJwt metody pomocniczej, która konfiguruje aplikację do weryfikowania tokenów JWT utworzonych przez usługę IdentityServer:

    builder.Services.AddAuthentication()
    .AddIdentityServerJwt();
    
  • Oprogramowanie pośredniczące do uwierzytelniania, które odpowiada za weryfikację poświadczeń żądania oraz ustawienie użytkownika w kontekście tego żądania.

    app.UseAuthentication();
    
  • Oprogramowanie pośredniczące IdentityServer, które uwidacznia punkty końcowe OpenID Connect:

    app.UseIdentityServer();
    

Ostrzeżenie

W tym artykule przedstawiono użycie parametrów połączenia. W przypadku lokalnej bazy danych użytkownik nie musi być uwierzytelniany, ale w środowisku produkcyjnym parametry połączenia czasami zawiera hasło do uwierzytelniania. Poświadczenie hasła właściciela zasobu (ROPC) jest zagrożeniem bezpieczeństwa, którego należy unikać w produkcyjnych bazach danych. Aplikacje produkcyjne powinny korzystać z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Aby uzyskać więcej informacji na temat uwierzytelniania aplikacji wdrożonych w środowiskach testowych lub produkcyjnych, zobacz Bezpieczne przepływy uwierzytelniania.

usługa Azure App Service na systemie Linux

W przypadku wdrożeń usługi Azure App Service na Linuxie określ wystawcę jawnie.

builder.Services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme, 
    options =>
    {
        options.Authority = "{AUTHORITY}";
    });

W poprzednim kodzie symbol {AUTHORITY} jest zastępczym miejscem dla Authority, używanym podczas wykonywania wywołań OpenID Connect.

Przykład:

options.Authority = "https://contoso-service.azurewebsites.net";

AddApiAuthorization

Ta metoda pomocnicza konfiguruje IdentityServer do korzystania z obsługiwanej przez nas konfiguracji. IdentityServer to zaawansowana i rozszerzalna struktura do obsługi problemów z zabezpieczeniami aplikacji. Jednocześnie uwidacznia niepotrzebną złożoność dla najbardziej typowych scenariuszy. W związku z tym dostępny jest zestaw konwencji i opcji konfiguracji, które są uważane za dobry punkt wyjścia. Po zmianie potrzeb uwierzytelniania pełna moc serwera IdentityServer jest nadal dostępna w celu dostosowania uwierzytelniania zgodnie z potrzebami.

AddIdentityServerJwt

Ta metoda pomocnika konfiguruje schemat zasad dla aplikacji jako domyślną procedurę obsługi uwierzytelniania. Zasady są skonfigurowane tak, aby umożliwić Identity obsługę wszystkich żądań kierowanych do dowolnej ścieżki podrzędnej Identity w obszarze adresu URL "/Identity". Usługa JwtBearerHandler obsługuje wszystkie inne żądania. Ponadto ta metoda rejestruje zasób interfejsu <<ApplicationName>>API API w IdentityServer z domyślnym zakresem <<ApplicationName>>API i konfiguruje middleware tokenu bearer JWT do weryfikacji tokenów wystawionych przez IdentityServer dla aplikacji.

WeatherForecastController

W pliku zwróć uwagę [Authorize] na atrybut zastosowany do klasy, który wskazuje, że użytkownik musi być autoryzowany na podstawie domyślnych zasad dostępu do zasobu. Domyślna polityka autoryzacji jest skonfigurowana w taki sposób, że przypadkowo używa domyślnego schematu uwierzytelniania, który AddIdentityServerJwt ustawia jako schemat polityki wspomniany powyżej. Dzięki temu JwtBearerHandler skonfigurowana przez taką metodę pomocnika staje się domyślną obsługą żądań do aplikacji.

ApplicationDbContext

W pliku zwróć uwagę, że to samo DbContext jest używane w Identity, z wyjątkiem tego, że rozszerza ApiAuthorizationDbContext (jako bardziej pochodną klasę z IdentityDbContext), aby uwzględnić schemat dla IdentityServer.

Aby uzyskać pełną kontrolę nad schematem bazy danych, odziedzicz z jednej z dostępnych IdentityDbContext klas i skonfiguruj kontekst, aby uwzględnić Identity schemat poprzez wywołanie metody builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) na OnModelCreating.

OidcConfigurationController

Zwróć uwagę na punkt końcowy w pliku, który jest przygotowany do obsługi parametrów OIDC, których klient musi użyć.

appsettings.json

appsettings.json W pliku głównym projektu znajduje się nowa IdentityServer sekcja, która opisuje listę skonfigurowanych klientów. W poniższym przykładzie istnieje jeden klient. Nazwa klienta odpowiada nazwie aplikacji i jest mapowana zgodnie z konwencją do parametru OAuth ClientId . Profil wskazuje skonfigurowany typ aplikacji. Jest ona używana wewnętrznie do napędzania konwencji, które upraszczają proces konfiguracji serwera. Istnieje kilka dostępnych profilów, jak wyjaśniono w sekcji Profile aplikacji.

"IdentityServer": {
  "Clients": {
    "angularindividualpreview3final": {
      "Profile": "IdentityServerSPA"
    }
  }
}

appsettings.Development.json

W pliku głównym projektu appsettings.Development.json znajduje się sekcja IdentityServer opisująca klucz używany do podpisywania tokenów. Podczas wdrażania w środowisku produkcyjnym należy aprowizować i wdrażać klucz obok aplikacji, jak wyjaśniono w sekcji Wdrażanie w środowisku produkcyjnym .

"IdentityServer": {
  "Key": {
    "Type": "Development"
  }
}

Ogólny opis aplikacji Angular

Obsługa uwierzytelniania i autoryzacji interfejsu API w szablonie usługi Angular znajduje się we własnym module Angular w katalogu ClientApp/src/api-authorization . Moduł składa się z następujących elementów:

  • 3 składniki:
    • login.component.ts: obsługuje przepływ logowania aplikacji.
    • logout.component.ts: obsługuje proces wylogowywania aplikacji.
    • login-menu.component.ts: widżet, który wyświetla jeden z następujących zestawów łączy:
      • Zarządzanie profilem użytkownika i link do wylogowania, gdy użytkownik jest uwierzytelniony.
      • Linki do rejestracji i logowania, gdy użytkownik nie jest uwierzytelniony.
  • Zabezpieczenie AuthorizeGuard trasy, które można dodać i które wymaga uwierzytelnienia użytkownika przed odwiedzeniem trasy.
  • Przechwytownik AuthorizeInterceptor HTTP, który dołącza token dostępu do wychodzących żądań HTTP kierowanych do interfejsu API, gdy użytkownik jest uwierzytelniony.
  • Usługa AuthorizeService, która obsługuje szczegóły niższego poziomu procesu uwierzytelniania i uwidacznia informacje o uwierzytelnianym użytkowniku w pozostałej części aplikacji do użycia.
  • Moduł Angular, który definiuje trasy skojarzone z częściami uwierzytelniania aplikacji. Udostępnia składnik menu logowania, przechwytywacz, zabezpieczenie i usługę do wykorzystania przez resztę aplikacji.

Ogólny opis aplikacji React

Obsługa uwierzytelniania i autoryzacji interfejsu API w szablonie react znajduje się w katalogu ClientApp/src/components/api-authorization . Składa się z następujących elementów:

  • 4 składniki:
    • Login.js: obsługuje przepływ logowania aplikacji.
    • Logout.js: obsługuje proces wylogowywania aplikacji.
    • LoginMenu.js: widżet, który wyświetla jeden z następujących zestawów łączy:
      • Zarządzanie profilem użytkownika i link do wylogowania, gdy użytkownik jest uwierzytelniony.
      • Linki do rejestracji i logowania, gdy użytkownik nie jest uwierzytelniony.
    • AuthorizeRoute.js: składnik trasy, który wymaga uwierzytelnienia użytkownika przed renderowaniem składnika wskazanego w parametrze Component .
  • Wyeksportowane authService wystąpienie klasy AuthorizeService, które obsługuje szczegóły procesu uwierzytelniania niższego poziomu i uwidacznia informacje o uwierzytelnianym użytkowniku w pozostałej części aplikacji do użycia.

Po zapoznaniu się z głównymi składnikami rozwiązania możesz dokładniej przyjrzeć się poszczególnym scenariuszom aplikacji.

Wymaganie autoryzacji w nowym interfejsie API

Domyślnie system jest skonfigurowany tak, aby można było łatwo wymagać autoryzacji dla nowych interfejsów API. W tym celu utwórz nowy kontroler i dodaj [Authorize] atrybut do klasy kontrolera lub dowolnej akcji w obrębie kontrolera.

Dostosowywanie procedury obsługi uwierzytelniania interfejsu API

Aby dostosować konfigurację programu obsługi JWT API, skonfiguruj jego wystąpienie JwtBearerOptions.

builder.Services.AddAuthentication()
    .AddIdentityServerJwt();

builder.Services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
    options =>
    {
        ...
    });

Procedura obsługi JWT interfejsu API zgłasza zdarzenia, które umożliwiają kontrolę nad procesem uwierzytelniania przy użyciu polecenia JwtBearerEvents. Aby zapewnić obsługę autoryzacji interfejsu API, AddIdentityServerJwt rejestruje własne procedury obsługi zdarzeń.

Aby dostosować obsługę zdarzenia, owiń istniejący obsługiwacz zdarzeń dodatkową logiką według potrzeb. Na przykład:

builder.Services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
    options =>
    {
        var onTokenValidated = options.Events.OnTokenValidated;       

        options.Events.OnTokenValidated = async context =>
        {
            await onTokenValidated(context);
            ...
        }
    });

W poprzednim kodzie OnTokenValidated procedura obsługi zdarzenia jest zastępowana implementacją niestandardową. Ta implementacja:

  1. Wywołuje oryginalną implementację zapewnianą przez obsługę autoryzacji interfejsu API.
  2. Uruchamianie własnej logiki niestandardowej.

Ochrona ścieżki po stronie klienta (Angular)

Ochrona trasy po stronie klienta jest wykonywana przez dodanie ochrony autoryzacji do listy zabezpieczeń do uruchomienia podczas konfigurowania trasy. Na przykład możesz zobaczyć, jak trasa fetch-data jest skonfigurowana w głównym module aplikacji Angular.

RouterModule.forRoot([
  // ...
  { path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])

Należy pamiętać, że ochrona trasy nie chroni rzeczywistego punktu końcowego (który nadal wymaga zastosowanego [Authorize] atrybutu), ale uniemożliwia użytkownikowi przejście do danej trasy po stronie klienta, gdy nie jest uwierzytelniony.

Uwierzytelnianie żądań interfejsu API (Angular)

Uwierzytelnianie żądań do interfejsów API hostowanych razem z aplikacją odbywa się automatycznie za pomocą przechwytnika klienta HTTP zdefiniowanego przez aplikację.

Zabezpieczenie ścieżki po stronie klienta (React)

Chroń trasę po stronie klienta przy użyciu AuthorizeRoute składnika zamiast zwykłego Route składnika. Zwróć na przykład uwagę na to, jak fetch-data trasa jest skonfigurowana w składniku App :

<AuthorizeRoute path='/fetch-data' component={FetchData} />

Ochrona trasy:

  • Nie chroni rzeczywistego punktu końcowego (który nadal wymaga zastosowanego atrybutu [Authorize] ).
  • Uniemożliwia użytkownikowi przejście do danej trasy po stronie klienta, tylko wtedy gdy nie jest uwierzytelniony.

Uwierzytelnianie żądań interfejsu API (React)

Uwierzytelnianie żądań w React odbywa się najpierw przez zaimportowanie wystąpienia authService z AuthorizeService. Token dostępu jest pobierany z authService obiektu i jest dołączony do żądania, jak pokazano poniżej. W komponentach React ta praca jest zwykle wykonywana w componentDidMount metodzie cyklu życia lub w wyniku interakcji z użytkownikiem.

Importowanie elementu authService do składnika

import authService from './api-authorization/AuthorizeService'

Pobieranie i dołączanie tokenu dostępu do odpowiedzi

async populateWeatherData() {
  const token = await authService.getAccessToken();
  const response = await fetch('api/SampleData/WeatherForecasts', {
    headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
  });
  const data = await response.json();
  this.setState({ forecasts: data, loading: false });
}

Zaimplementuj na produkcji

Aby wdrożyć aplikację w środowisku produkcyjnym, należy aprowizować następujące zasoby:

  • Baza danych służąca do przechowywania kont użytkowników oraz uprawnień przyznawanych przez IdentityServer.
  • Certyfikat produkcyjny do podpisywania tokenów.
    • Nie ma żadnych konkretnych wymagań dotyczących tego certyfikatu; może to być certyfikat z podpisem własnym lub certyfikat aprowizowany za pośrednictwem urzędu certyfikacji.
    • Można go wygenerować za pomocą standardowych narzędzi, takich jak Program PowerShell lub OpenSSL.
    • Można go zainstalować w magazynie certyfikatów na maszynach docelowych lub wdrożyć jako plik pfx z silnym hasłem.

Przykład: wdrażanie u dostawcy hostingu internetowego spoza platformy Azure

W panelu hostingu internetowego utwórz lub załaduj certyfikat. Następnie w pliku aplikacji appsettings.json zmodyfikuj IdentityServer sekcję w celu uwzględnienia kluczowych szczegółów. Na przykład:

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "WebHosting",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}

W powyższym przykładzie:

  • StoreName reprezentuje nazwę magazynu certyfikatów, w którym jest przechowywany certyfikat. W takim przypadku wskazuje on sklep hostingu internetowego.
  • StoreLocation reprezentuje miejsce ładowania certyfikatu z (CurrentUser w tym przypadku).
  • Name odnosi się do wyróżniającego się podmiotu certyfikatu.

Przykład: Wdrażanie do usługi Azure App Service

W tej sekcji opisano wdrażanie aplikacji w usłudze aplikacja systemu Azure przy użyciu certyfikatu przechowywanego w magazynie certyfikatów. Aby zmodyfikować aplikację w celu załadowania certyfikatu z magazynu certyfikatów, wymagany jest plan usługi poziomu Standard lub lepszy przy konfigurowaniu aplikacji w portalu Azure w kolejnym kroku.

W pliku aplikacji appsettings.json zmodyfikuj IdentityServer sekcję, aby zawierała kluczowe szczegóły:

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "My",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}
  • Nazwa magazynu reprezentuje nazwę magazynu certyfikatów, w którym jest przechowywany certyfikat. W takim przypadku wskazuje na osobistą przestrzeń przechowywania użytkownika.
  • Lokalizacja sklepu reprezentuje miejsce załadowania certyfikatu z (CurrentUser lub LocalMachine).
  • Właściwość nazwa certyfikatu odpowiada wyróżnionemu podmiotowi certyfikatu.

Aby wdrożyć aplikację w usłudze Azure App Service, wykonaj kroki opisane w Wdrożenie aplikacji na platformie Azure, które wyjaśniają, jak utworzyć niezbędne zasoby platformy Azure i wdrożyć aplikację w środowisku produkcyjnym.

Po wykonaniu powyższych instrukcji aplikacja zostanie wdrożona na platformie Azure, ale nie jest jeszcze funkcjonalna. Certyfikat używany przez aplikację musi być skonfigurowany w witrynie Azure Portal. Znajdź odcisk palca certyfikatu i wykonaj kroki opisane w temacie Ładowanie certyfikatów.

Chociaż te kroki obejmują protokół SSL, w witrynie Azure Portal znajduje się sekcja Certyfikaty prywatne, w której można przekazać aprowizowany certyfikat do użycia z aplikacją.

Po skonfigurowaniu aplikacji i ustawień aplikacji w witrynie Azure Portal uruchom ponownie aplikację w portalu.

Inne opcje konfiguracji

Obsługa autoryzacji interfejsu API opiera się na IdentityServer z zestawem konwencji, wartości domyślnych i ulepszeń, aby uprościć doświadczenie dla SPA. Nie trzeba dodawać, że pełna moc IdentityServer jest dostępna za kulisami, jeśli integracje z ASP.NET Core nie pokrywają twojego scenariusza. Obsługa platformy ASP.NET Core koncentruje się na aplikacjach "firmowych", w których wszystkie aplikacje są tworzone i wdrażane przez naszą organizację. W związku z tym pomoc techniczna nie jest oferowana dla takich kwestii jak zgoda lub federacja. W tych scenariuszach, użyj IdentityServer i postępuj zgodnie z ich dokumentacją.

Profile aplikacji

Profile aplikacji to wstępnie zdefiniowane konfiguracje aplikacji, które dodatkowo definiują ich parametry. Obecnie obsługiwane są następujące profile:

  • IdentityServerSPA: reprezentuje SPA hostowany obok IdentityServer jako pojedynczą jednostkę.
    • Wartość domyślna redirect_uri to /authentication/login-callback.
    • Wartość domyślna post_logout_redirect_uri to /authentication/logout-callback.
    • Zestaw zakresów obejmuje openid, profile oraz każdy zakres zdefiniowany dla interfejsów API w aplikacji.
    • Zestaw dozwolonych typów odpowiedzi OIDC jest id_token token lub każdy z nich indywidualnie (id_token, token).
    • Dozwolony tryb odpowiedzi to fragment.
  • SPA: reprezentuje SPA, który nie jest hostowany w usłudze IdentityServer.
    • Zestaw zakresów obejmuje openid, profile oraz każdy zakres zdefiniowany dla interfejsów API w aplikacji.
    • Zestaw dozwolonych typów odpowiedzi OIDC jest id_token token lub każdy z nich indywidualnie (id_token, token).
    • Dozwolony tryb odpowiedzi to fragment.
  • IdentityServerJwt: reprezentuje interfejs API hostowany razem z usługą IdentityServer.
    • Aplikacja jest skonfigurowana tak, aby miała jeden zakres, który domyślnie ma nazwę aplikacji.
  • API: reprezentuje interfejs API, który nie jest hostowany w usłudze IdentityServer.
    • Aplikacja jest skonfigurowana tak, aby miała jeden zakres, który domyślnie ma nazwę aplikacji.

Konfiguracja za pośrednictwem AppSettings

Skonfiguruj aplikacje za pomocą systemu konfiguracji, dodając je do listy Clients lub Resources.

Skonfiguruj właściwości redirect_uri i post_logout_redirect_uri każdego klienta, jak pokazano w poniższym przykładzie:

"IdentityServer": {
  "Clients": {
    "MySPA": {
      "Profile": "SPA",
      "RedirectUri": "https://www.example.com/authentication/login-callback",
      "LogoutUri": "https://www.example.com/authentication/logout-callback"
    }
  }
}

Podczas konfigurowania zasobów można skonfigurować zakresy dla zasobu, jak pokazano poniżej:

"IdentityServer": {
  "Resources": {
    "MyExternalApi": {
      "Profile": "API",
      "Scopes": "a b c"
    }
  }
}

Konfiguracja za pomocą kodu

Można również skonfigurować klientów i zasoby za pomocą kodu przy użyciu przeciążenia AddApiAuthorization , które podejmuje akcję w celu skonfigurowania opcji.

AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
    options.Clients.AddSPA(
        "My SPA", spa =>
        spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
           .WithLogoutRedirectUri(
               "http://www.example.com/authentication/logout-callback"));

    options.ApiResources.AddApiResource("MyExternalApi", resource =>
        resource.WithScopes("a", "b", "c"));
});

Dodatkowe zasoby

Szablony ASP.NET Core 3.1 i nowszych oferują uwierzytelnianie w aplikacjach jednostronicowych (SPAs) przy użyciu wsparcia autoryzacji API. ASP.NET Core Identity na potrzeby uwierzytelniania i przechowywania użytkowników jest łączona z usługą IdentityServer na potrzeby implementowania programu OpenID Connect.

Parametr uwierzytelniania został dodany do szablonów projektów Angular i React, podobny do parametru uwierzytelniania w szablonach projektów aplikacji internetowej (Model-View-Controller) oraz aplikacji internetowej (Razor Pages). Dozwolone wartości parametrów to None i Individual. Szablon projektu React.js i Redux nie obsługuje obecnie parametru uwierzytelniania.

Tworzenie aplikacji z obsługą autoryzacji interfejsu API

Uwierzytelnianie i autoryzacja użytkownika mogą być używane zarówno z usługami Angular, jak i React SPA. Otwórz konsolę i uruchom następujące polecenie:

Angular:

dotnet new angular -o <output_directory_name> 

React:

dotnet new react -o <output_directory_name> -au Individual

Poprzednie polecenie tworzy aplikację ASP.NET Core z katalogiem ClientApp zawierającym SPA.

Ogólny opis składników ASP.NET Core aplikacji

W poniższych sekcjach opisano dodatki do projektu, gdy jest uwzględniona obsługa uwierzytelniania:

Klasa Startup

Poniższe przykłady kodu bazują na pakiecie NuGet Microsoft.AspNetCore.ApiAuthorization.IdentityServer . Przykłady konfigurują uwierzytelnianie i autoryzację API przy użyciu metod rozszerzeń AddApiAuthorization i AddIdentityServerJwt. Projekty korzystające z szablonów projektów React lub Angular SPA z uwierzytelnianiem obejmują odwołanie do tego pakietu.

Klasa Startup ma następujące dodatki:

  • Wewnątrz Startup.ConfigureServices metody:

    • Identity z domyślnym interfejsem użytkownika:

      services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
      
      services.AddDefaultIdentity<ApplicationUser>()
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • IdentityServer z dodatkową AddApiAuthorization metodą pomocnika, która konfiguruje niektóre domyślne konwencje ASP.NET Core na serwerze IdentityServer:

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Uwierzytelnianie przy użyciu dodatkowej AddIdentityServerJwt metody pomocniczej, która konfiguruje aplikację do weryfikowania tokenów JWT utworzonych przez usługę IdentityServer:

      services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • Wewnątrz Startup.Configure metody:

    • Oprogramowanie pośredniczące do uwierzytelniania, które odpowiada za weryfikację poświadczeń żądania i przypisanie użytkownika do kontekstu żądania.

      app.UseAuthentication();
      
    • Oprogramowanie pośredniczące IdentityServer, które uwidacznia punkty końcowe OpenID Connect:

      app.UseIdentityServer();
      

Ostrzeżenie

W tym artykule przedstawiono użycie parametrów połączenia. W przypadku lokalnej bazy danych użytkownik nie musi być uwierzytelniany, ale w środowisku produkcyjnym parametry połączenia czasami zawiera hasło do uwierzytelniania. Poświadczenie hasła właściciela zasobu (ROPC) jest zagrożeniem bezpieczeństwa, którego należy unikać w produkcyjnych bazach danych. Aplikacje produkcyjne powinny korzystać z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Aby uzyskać więcej informacji na temat uwierzytelniania aplikacji wdrożonych w środowiskach testowych lub produkcyjnych, zobacz Bezpieczne przepływy uwierzytelniania.

usługa Azure App Service na systemie Linux

W przypadku wdrożeń Azure App Service na systemie Linux, określ wystawcę jawnie w pliku Startup.ConfigureServices:

services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme, 
    options =>
    {
        options.Authority = "{AUTHORITY}";
    });

W poprzednim kodzie symbol {AUTHORITY} jest zastępczym miejscem dla Authority, używanym podczas wykonywania wywołań OpenID Connect.

Przykład:

options.Authority = "https://contoso-service.azurewebsites.net";

AddApiAuthorization

Ta metoda pomocnicza konfiguruje IdentityServer do korzystania z obsługiwanej przez nas konfiguracji. IdentityServer to zaawansowana i rozszerzalna struktura do obsługi problemów z zabezpieczeniami aplikacji. Jednocześnie uwidacznia niepotrzebną złożoność dla najbardziej typowych scenariuszy. W związku z tym dostępny jest zestaw konwencji i opcji konfiguracji, które są uważane za dobry punkt wyjścia. Po zmianie potrzeb uwierzytelniania pełna moc serwera IdentityServer jest nadal dostępna w celu dostosowania uwierzytelniania zgodnie z potrzebami.

AddIdentityServerJwt

Ta metoda pomocnika konfiguruje schemat zasad dla aplikacji jako domyślną procedurę obsługi uwierzytelniania. Zasady są skonfigurowane tak, aby umożliwić Identity obsługę wszystkich żądań kierowanych do dowolnej ścieżki podrzędnej Identity w obszarze adresu URL "/Identity". Usługa JwtBearerHandler obsługuje wszystkie inne żądania. Ponadto ta metoda rejestruje zasób interfejsu <<ApplicationName>>API API w IdentityServer z domyślnym zakresem <<ApplicationName>>API i konfiguruje middleware tokenu bearer JWT do weryfikacji tokenów wystawionych przez IdentityServer dla aplikacji.

WeatherForecastController

W pliku zwróć uwagę [Authorize] na atrybut zastosowany do klasy, który wskazuje, że użytkownik musi być autoryzowany na podstawie domyślnych zasad dostępu do zasobu. Domyślna polityka autoryzacji jest skonfigurowana w taki sposób, że przypadkowo używa domyślnego schematu uwierzytelniania, który AddIdentityServerJwt ustawia jako schemat polityki wspomniany powyżej. Dzięki temu JwtBearerHandler skonfigurowana przez taką metodę pomocnika staje się domyślną obsługą żądań do aplikacji.

ApplicationDbContext

W pliku zwróć uwagę, że to samo DbContext jest używane w Identity, z wyjątkiem tego, że rozszerza ApiAuthorizationDbContext (jako bardziej pochodną klasę z IdentityDbContext), aby uwzględnić schemat dla IdentityServer.

Aby uzyskać pełną kontrolę nad schematem bazy danych, odziedzicz z jednej z dostępnych IdentityDbContext klas i skonfiguruj kontekst, aby uwzględnić Identity schemat poprzez wywołanie metody builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) na OnModelCreating.

OidcConfigurationController

Zwróć uwagę na punkt końcowy w pliku, który jest przygotowany do obsługi parametrów OIDC, których klient musi użyć.

appsettings.json

appsettings.json W pliku głównym projektu znajduje się nowa IdentityServer sekcja, która opisuje listę skonfigurowanych klientów. W poniższym przykładzie istnieje jeden klient. Nazwa klienta odpowiada nazwie aplikacji i jest mapowana zgodnie z konwencją do parametru OAuth ClientId . Profil wskazuje skonfigurowany typ aplikacji. Jest ona używana wewnętrznie do napędzania konwencji, które upraszczają proces konfiguracji serwera. Istnieje kilka dostępnych profilów, jak wyjaśniono w sekcji Profile aplikacji.

"IdentityServer": {
  "Clients": {
    "angularindividualpreview3final": {
      "Profile": "IdentityServerSPA"
    }
  }
}

appsettings.Development.json

W pliku głównym projektu appsettings.Development.json znajduje się sekcja IdentityServer opisująca klucz używany do podpisywania tokenów. Podczas wdrażania w środowisku produkcyjnym należy aprowizować i wdrażać klucz obok aplikacji, jak wyjaśniono w sekcji Wdrażanie w środowisku produkcyjnym .

"IdentityServer": {
  "Key": {
    "Type": "Development"
  }
}

Ogólny opis aplikacji Angular

Obsługa uwierzytelniania i autoryzacji interfejsu API w szablonie usługi Angular znajduje się we własnym module Angular w katalogu ClientApp/src/api-authorization . Moduł składa się z następujących elementów:

  • 3 składniki:
    • login.component.ts: obsługuje przepływ logowania aplikacji.
    • logout.component.ts: obsługuje proces wylogowywania aplikacji.
    • login-menu.component.ts: widżet, który wyświetla jeden z następujących zestawów łączy:
      • Zarządzanie profilem użytkownika i link do wylogowania, gdy użytkownik jest uwierzytelniony.
      • Linki do rejestracji i logowania, gdy użytkownik nie jest uwierzytelniony.
  • Zabezpieczenie AuthorizeGuard trasy, które można dodać i które wymaga uwierzytelnienia użytkownika przed odwiedzeniem trasy.
  • Przechwytownik AuthorizeInterceptor HTTP, który dołącza token dostępu do wychodzących żądań HTTP przeznaczonych dla interfejsu API, gdy użytkownik jest uwierzytelniony.
  • Usługa AuthorizeService, która obsługuje szczegóły niższego poziomu procesu uwierzytelniania i uwidacznia informacje o uwierzytelnianym użytkowniku w pozostałej części aplikacji do użycia.
  • Moduł Angular, który definiuje trasy skojarzone z częściami uwierzytelniania aplikacji. Udostępnia składnik menu logowania, przechwytywacz, zabezpieczenie i usługę do wykorzystania przez resztę aplikacji.

Ogólny opis aplikacji React

Obsługa uwierzytelniania i autoryzacji interfejsu API w szablonie react znajduje się w katalogu ClientApp/src/components/api-authorization . Składa się z następujących elementów:

  • 4 składniki:
    • Login.js: obsługuje przepływ logowania aplikacji.
    • Logout.js: obsługuje proces wylogowywania aplikacji.
    • LoginMenu.js: widżet, który wyświetla jeden z następujących zestawów łączy:
      • Zarządzanie profilem użytkownika i link do wylogowania, gdy użytkownik jest uwierzytelniony.
      • Linki do rejestracji i logowania, gdy użytkownik nie jest uwierzytelniony.
    • AuthorizeRoute.js: składnik trasy, który wymaga uwierzytelnienia użytkownika przed renderowaniem składnika wskazanego w parametrze Component .
  • Wyeksportowane authService wystąpienie klasy AuthorizeService, które obsługuje szczegóły procesu uwierzytelniania niższego poziomu i uwidacznia informacje o uwierzytelnionym użytkowniku dla pozostałej części aplikacji do wykorzystania.

Po zapoznaniu się z głównymi składnikami rozwiązania możesz dokładniej przyjrzeć się poszczególnym scenariuszom aplikacji.

Wymaganie autoryzacji w nowym interfejsie API

Domyślnie system jest skonfigurowany tak, aby można było łatwo wymagać autoryzacji dla nowych interfejsów API. W tym celu utwórz nowy kontroler i dodaj [Authorize] atrybut do klasy kontrolera lub dowolnej akcji w obrębie kontrolera.

Dostosuj moduł obsługi uwierzytelniania API

Aby dostosować konfigurację programu obsługi JWT API, skonfiguruj jego wystąpienie JwtBearerOptions.

services.AddAuthentication()
    .AddIdentityServerJwt();

services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
    options =>
    {
        ...
    });

Procedura obsługi JWT interfejsu API zgłasza zdarzenia, które umożliwiają kontrolę nad procesem uwierzytelniania przy użyciu polecenia JwtBearerEvents. Aby zapewnić obsługę autoryzacji interfejsu API, AddIdentityServerJwt rejestruje własne procedury obsługi zdarzeń.

Aby dostosować obsługę zdarzenia, owiń istniejący obsługiwacz zdarzeń dodatkową logiką według potrzeb. Na przykład:

services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
    options =>
    {
        var onTokenValidated = options.Events.OnTokenValidated;       

        options.Events.OnTokenValidated = async context =>
        {
            await onTokenValidated(context);
            ...
        }
    });

W poprzednim kodzie OnTokenValidated procedura obsługi zdarzenia jest zastępowana implementacją niestandardową. Ta implementacja:

  1. Wywołuje oryginalną implementację zapewnianą przez obsługę autoryzacji interfejsu API.
  2. Uruchom własną niestandardową logikę.

Ochrona ścieżki po stronie klienta (Angular)

Ochrona trasy po stronie klienta jest wykonywana przez dodanie ochrony autoryzacji do listy zabezpieczeń do uruchomienia podczas konfigurowania trasy. Na przykład możesz zobaczyć, jak trasa fetch-data jest skonfigurowana w głównym module aplikacji Angular.

RouterModule.forRoot([
  // ...
  { path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])

Należy pamiętać, że ochrona trasy nie chroni rzeczywistego punktu końcowego (który nadal wymaga zastosowanego [Authorize] atrybutu), ale uniemożliwia użytkownikowi przejście do danej trasy po stronie klienta, gdy nie jest uwierzytelniony.

Uwierzytelnianie żądań interfejsu API (Angular)

Uwierzytelnianie żądań do interfejsów API hostowanych razem z aplikacją odbywa się automatycznie za pomocą przechwytnika klienta HTTP zdefiniowanego przez aplikację.

Zabezpieczenie ścieżki po stronie klienta (React)

Chroń trasę po stronie klienta przy użyciu AuthorizeRoute składnika zamiast zwykłego Route składnika. Zwróć na przykład uwagę na to, jak fetch-data trasa jest skonfigurowana w składniku App :

<AuthorizeRoute path='/fetch-data' component={FetchData} />

Ochrona trasy:

  • Nie chroni rzeczywistego punktu końcowego (który nadal wymaga zastosowanego atrybutu [Authorize] ).
  • Uniemożliwia użytkownikowi przejście do danej trasy po stronie klienta, tylko wtedy gdy nie jest uwierzytelniony.

Uwierzytelnianie żądań interfejsu API (React)

Uwierzytelnianie żądań w React odbywa się najpierw przez zaimportowanie wystąpienia authService z AuthorizeService. Token dostępu jest pobierany z authService obiektu i jest dołączony do żądania, jak pokazano poniżej. W komponentach React ta praca jest zwykle wykonywana w componentDidMount metodzie cyklu życia lub w wyniku interakcji z użytkownikiem.

Importowanie elementu authService do składnika

import authService from './api-authorization/AuthorizeService'

Pobieranie i dołączanie tokenu dostępu do odpowiedzi

async populateWeatherData() {
  const token = await authService.getAccessToken();
  const response = await fetch('api/SampleData/WeatherForecasts', {
    headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
  });
  const data = await response.json();
  this.setState({ forecasts: data, loading: false });
}

Zaimplementuj na produkcji

Aby wdrożyć aplikację w środowisku produkcyjnym, należy aprowizować następujące zasoby:

  • Baza danych służąca do przechowywania kont użytkowników oraz uprawnień przyznawanych przez IdentityServer.
  • Certyfikat produkcyjny do podpisywania tokenów.
    • Nie ma żadnych konkretnych wymagań dotyczących tego certyfikatu; może to być certyfikat z podpisem własnym lub certyfikat aprowizowany za pośrednictwem urzędu certyfikacji.
    • Można go wygenerować za pomocą standardowych narzędzi, takich jak Program PowerShell lub OpenSSL.
    • Można go zainstalować w magazynie certyfikatów na maszynach docelowych lub wdrożyć jako plik pfx z silnym hasłem.

Przykład: wdrażanie u dostawcy hostingu internetowego spoza platformy Azure

W panelu hostingu internetowego utwórz lub załaduj certyfikat. Następnie w pliku aplikacji appsettings.json zmodyfikuj IdentityServer sekcję w celu uwzględnienia kluczowych szczegółów. Na przykład:

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "WebHosting",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}

W powyższym przykładzie:

  • StoreName reprezentuje nazwę magazynu certyfikatów, w którym jest przechowywany certyfikat. W takim przypadku wskazuje on sklep hostingu internetowego.
  • StoreLocation reprezentuje miejsce ładowania certyfikatu z (CurrentUser w tym przypadku).
  • Name odnosi się do wyróżniającego się podmiotu certyfikatu.

Przykład: Wdrażanie do usługi Azure App Service

W tej sekcji opisano wdrażanie aplikacji w usłudze aplikacja systemu Azure przy użyciu certyfikatu przechowywanego w magazynie certyfikatów. Aby zmodyfikować aplikację w celu załadowania certyfikatu z magazynu certyfikatów, wymagany jest plan usługi poziomu Standard lub lepszy przy konfigurowaniu aplikacji w portalu Azure w kolejnym kroku.

W pliku aplikacji appsettings.json zmodyfikuj IdentityServer sekcję, aby zawierała kluczowe szczegóły:

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "My",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}
  • Nazwa magazynu reprezentuje nazwę magazynu certyfikatów, w którym jest przechowywany certyfikat. W takim przypadku wskazuje na osobistą przestrzeń przechowywania użytkownika.
  • Lokalizacja sklepu reprezentuje miejsce załadowania certyfikatu z (CurrentUser lub LocalMachine).
  • Właściwość nazwa certyfikatu odpowiada wyróżnionemu podmiotowi certyfikatu.

Aby wdrożyć aplikację w usłudze Azure App Service, wykonaj kroki opisane w Wdrożenie aplikacji na platformie Azure, które wyjaśniają, jak utworzyć niezbędne zasoby platformy Azure i wdrożyć aplikację w środowisku produkcyjnym.

Po wykonaniu powyższych instrukcji aplikacja zostanie wdrożona na platformie Azure, ale nie jest jeszcze funkcjonalna. Certyfikat używany przez aplikację musi być skonfigurowany w witrynie Azure Portal. Znajdź odcisk palca certyfikatu i wykonaj kroki opisane w temacie Ładowanie certyfikatów.

Chociaż te kroki obejmują protokół SSL, w witrynie Azure Portal znajduje się sekcja Certyfikaty prywatne, w której można przekazać aprowizowany certyfikat do użycia z aplikacją.

Po skonfigurowaniu aplikacji i ustawień aplikacji w witrynie Azure Portal uruchom ponownie aplikację w portalu.

Inne opcje konfiguracji

Obsługa autoryzacji interfejsu API opiera się na maszynie wirtualnej IdentityServer z zestawem konwencji, wartości domyślnych i ulepszeń, aby uprościć środowisko dla spAs. Nie trzeba dodawać, że pełna moc IdentityServer jest dostępna za kulisami, jeśli integracje z ASP.NET Core nie pokrywają twojego scenariusza. Obsługa platformy ASP.NET Core koncentruje się na aplikacjach "firmowych", w których wszystkie aplikacje są tworzone i wdrażane przez naszą organizację. W związku z tym pomoc techniczna nie jest oferowana dla takich kwestii jak zgoda lub federacja. W tych scenariuszach, użyj IdentityServer i postępuj zgodnie z ich dokumentacją.

Profile aplikacji

Profile aplikacji to wstępnie zdefiniowane konfiguracje aplikacji, które dodatkowo definiują ich parametry. Obecnie obsługiwane są następujące profile:

  • IdentityServerSPA: reprezentuje SPA hostowany obok IdentityServer jako pojedynczą jednostkę.
    • Wartość domyślna redirect_uri to /authentication/login-callback.
    • Wartość domyślna post_logout_redirect_uri to /authentication/logout-callback.
    • Zestaw zakresów obejmuje openid, profile oraz każdy zakres zdefiniowany dla interfejsów API w aplikacji.
    • Zestaw dozwolonych typów odpowiedzi OIDC jest id_token token lub każdy z nich indywidualnie (id_token, token).
    • Dozwolony tryb odpowiedzi to fragment.
  • SPA: reprezentuje SPA, który nie jest hostowany w usłudze IdentityServer.
    • Zestaw zakresów obejmuje openid, profile oraz każdy zakres zdefiniowany dla interfejsów API w aplikacji.
    • Zestaw dozwolonych typów odpowiedzi OIDC jest id_token token lub każdy z nich indywidualnie (id_token, token).
    • Dozwolony tryb odpowiedzi to fragment.
  • IdentityServerJwt: reprezentuje interfejs API hostowany razem z usługą IdentityServer.
    • Aplikacja jest skonfigurowana tak, aby miała jeden zakres, który domyślnie ma nazwę aplikacji.
  • API: reprezentuje interfejs API, który nie jest hostowany w usłudze IdentityServer.
    • Aplikacja jest skonfigurowana tak, aby miała jeden zakres, który domyślnie ma nazwę aplikacji.

Konfiguracja za pośrednictwem AppSettings

Skonfiguruj aplikacje za pomocą systemu konfiguracji, dodając je do listy Clients lub Resources.

Skonfiguruj właściwości redirect_uri i post_logout_redirect_uri każdego klienta, jak pokazano w poniższym przykładzie:

"IdentityServer": {
  "Clients": {
    "MySPA": {
      "Profile": "SPA",
      "RedirectUri": "https://www.example.com/authentication/login-callback",
      "LogoutUri": "https://www.example.com/authentication/logout-callback"
    }
  }
}

Podczas konfigurowania zasobów można skonfigurować zakresy dla zasobu, jak pokazano poniżej:

"IdentityServer": {
  "Resources": {
    "MyExternalApi": {
      "Profile": "API",
      "Scopes": "a b c"
    }
  }
}

Konfiguracja za pomocą kodu

Można również skonfigurować klientów i zasoby za pomocą kodu, korzystając z przeciążenia AddApiAuthorization, które wykonuje zadanie konfiguracji opcji.

AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
    options.Clients.AddSPA(
        "My SPA", spa =>
        spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
           .WithLogoutRedirectUri(
               "http://www.example.com/authentication/logout-callback"));

    options.ApiResources.AddApiResource("MyExternalApi", resource =>
        resource.WithScopes("a", "b", "c"));
});

Dodatkowe zasoby