Jak zabezpieczyć Identity zaplecze internetowego interfejsu API dla spA

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

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ą, zapoznaj się z wersją tego artykułu platformy .NET 8.

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 internetowego interfejsu API przy użyciu cookieuwierzytelniania opartego na protokole . Istnieje opcja oparta na tokenach dla klientów, których nie można używać cookie.

W tym artykule przedstawiono sposób zabezpieczania Identity zaplecza internetowego interfejsu API dla spA, takich jak aplikacje Angular, React i Vue. Te same interfejsy API zaplecza mogą służyć do zabezpieczania Blazor WebAssembly aplikacji.

Wymagania wstępne

Kroki przedstawione w tym artykule dodają uwierzytelnianie i autoryzację do aplikacji internetowego interfejsu API platformy ASP.NET Core, która:

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

Niektóre instrukcje testowania w tym artykule używają interfejsu użytkownika struktury Swagger dołączonego do szablonu projektu. Interfejs użytkownika struktury Swagger nie jest wymagany do użycia Identity z zapleczem internetowego interfejsu 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ą, ponieważ wymaga utworzenia bazy danych za pośrednictwem migracji, jak pokazano w samouczku EF Corewprowadzającym.

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

Tworzenie elementu 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 co najmniej jedną z poniższych using dyrektyw zgodnie z potrzebami podczas dodawania kodu pokazanego w tych krokach.

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

EF Core Konfigurowanie kontekstu

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 używanie 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 metody wywołaj metodę WebApplication.CreateBuilder(args), wywołaj metodę AddAuthorization , aby dodać usługi do kontenera wstrzykiwania zależności (DI):

builder.Services.AddAuthorization();

Aktywowanie Identity interfejsów API

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

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

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

Mapuj Identity trasy

Po wywołaniu metody builder.Build()wywołania metody wywołaj metodę MapIdentityApi<TUser>(IEndpointRouteBuilder) , aby zamapować Identity punkty końcowe:

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 punktów końcowych interfejsu użytkownika struktury 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 zabezpieczanie punktów końcowych przez zastosowanie atrybutu [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ć interfejs API za pomocą interfejsu użytkownika struktury Swagger. Upewnij się, że punkty końcowe interfejsu użytkownika struktury Swagger 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 przez szablon internetowego interfejsu API.
  • Wybierz pozycję Wypróbuj.
  • Wybierz polecenie Wykonaj. Odpowiedź to 401 - not authorized.

Testowanie rejestracji

  • 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 niefanumeryczny

    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 JSwłaściwości ON nie są potrzebne w tym przykładzie i można je usunąć. Ustaw wartość opcji useCookies na true.

  • Zastąp ciąg ciąg adresem e-mail i hasłem użytym do zarejestrowania, a następnie wybierz pozycję Wykonaj.

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

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. CookieUwierzytelnianie oparte na protokole jest bezpiecznie wbudowane w przeglądarkę i "działa".

Testowanie przy użyciu klientów innych niżbrowser

Niektórzy klienci sieci Web mogą domyślnie nie zawierać cookies w nagłówku:

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

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

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

    request.SetBrowserRequestCredential(BrowserRequestCredentials.Include);
    

Korzystanie z uwierzytelniania opartego na tokenach

W przypadku klientów, którzy nie obsługują cookieusług, interfejs API logowania udostępnia parametr do żądania tokenów. 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. Dostępny 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 JStokenami internetowymi (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 klientów, którzy nie mogą używać cookietych usług.

Aby użyć uwierzytelniania opartego useCookies na tokenach, ustaw parametr ciągu zapytania na false wartość podczas wywoływania punktu końcowego /login . Tokeny używają schematu uwierzytelniania elementu nośnego. Użycie tokenu zwróconego z wywołania metody do /loginmetody , 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 JSobiekt ON ({}) 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 metody w celu MapIdentityApi<TUser> dodawania następujących punktów końcowych do aplikacji:

Korzystanie z punktu końcowego POST /register

Treść żądania musi mieć Email właściwości i Password :

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

Aby uzyskać więcej informacji, zobacz:

Korzystanie z punktu końcowego POST /login

W treści Email żądania i Password są wymagane. Jeśli włączono uwierzytelnianie dwuskładnikowe (2FA), TwoFactorCodeTwoFactorRecoveryCode albo jest wymagane. Jeśli uwierzytelnianie 2FA nie jest włączone, pomiń zarówno uwierzytelnianie, jak twoFactorCode 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 nie włączoną uwierzytelnianiem 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 wartość lub false pomiń uwierzytelnianie oparte 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 zostanie false pominięte lub 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 token dostępu wkrótce wygaśnie, wywołaj punkt końcowy /refresh .

Korzystanie z 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"
}

Korzystanie z punktu końcowego GET /confirmEmail

Jeśli Identity skonfigurowana jest opcja potwierdzenia wiadomości e-mail, pomyślne wywołanie /register punktu końcowego 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 potwierdzenie wiadomości e-mail, dodaj kod , Program.cs aby ustawić RequireConfirmedEmail wartość i true dodać klasę implementającą IEmailSender do kontenera DI. 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 jest to klasa, EmailSender która implementuje IEmailSenderelement . Aby uzyskać więcej informacji, w tym przykład klasy, która implementuje IEmailSenderprogram , zobacz Potwierdzanie konta i odzyskiwanie hasła w programie ASP.NET Core.

Korzystanie z 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 Używanie punktu końcowego GET /confirmEmail wcześniej w tym artykule.

Korzystanie z 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 na temat włączania Identity wysyłania wiadomości e-mail, zobacz Korzystanie z punktu końcowegoGET /confirmEmail.

Korzystanie z punktu końcowego POST /resetPassword

Wywołaj ten punkt końcowy po otrzymaniu kodu resetowania /forgotPassword , wywołując punkt końcowy.

Treść żądania wymaga , EmailResetCodei NewPassword. Oto przykład:

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

Korzystanie z 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ę wystawcy uwierzytelniania oprócz adresu e-mail i hasła.

Włączanie uwierzytelniania 2FA

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

  • Wywołaj /manage/2fa punkt końcowy, wysyłając pusty JSobiekt ON ({}) w treści żądania.

  • Treść odpowiedzi udostępnia SharedKey wraz z innymi właściwościami, które nie są w tym momencie 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 /manage/2fa punkt końcowy, wysyłając element 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 wystawcy uwierzytelniania 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 /login punkt końcowy, wysył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 wystawcy uwierzytelniania, zaloguj się, wywołując /login punkt końcowy przy użyciu jednego z kodów odzyskiwania podanych po włączeniu uwierzytelniania 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, wywołaj ten punkt końcowy z ustawionym 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 wyczyścić flagę cookie "zapamiętaj mnie", jeśli jest obecna, wywołaj ten punkt końcowy z ustawionym wartością ForgetMachine true. Oto przykład treści żądania:

{
  "forgetMachine": true
}

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

Korzystanie z 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 z tego punktu końcowego 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 Email właściwości i IsEmailConfirmed , jak w poniższym przykładzie:

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

Korzystanie z punktu końcowego POST /manage/info

Aktualizacje adres e-mail i hasło zalogowanego użytkownika. Wyślij NewEmailelement , NewPasswordi 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 Połączenie OpenID.

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 , które są podobne do parametru uwierzytelniania w szablonach projektów Aplikacji internetowej (Model-View-Controller) i 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 powłokę poleceń 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 kodzie Microsoft.AspNetCore.ApiAuthorization.IdentityPakiet NuGet serwera . Przykłady umożliwiają skonfigurowanie uwierzytelniania i autoryzacji interfejsu AddApiAuthorization API przy użyciu metod i AddIdentityServerJwt rozszerzeń. 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>();
    
  • IdentitySerwer z dodatkową AddApiAuthorization metodą pomocnika, która konfiguruje niektóre domyślne konwencje ASP.NET Core na serwerze Identity:

    builder.Services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
    
  • Uwierzytelnianie za pomocą dodatkowej AddIdentityServerJwt metody pomocniczej, która konfiguruje aplikację do weryfikowania tokenów JWT utworzonych przez Identityserwer:

    builder.Services.AddAuthentication()
    .AddIdentityServerJwt();
    
  • Oprogramowanie pośredniczące uwierzytelniania, które jest odpowiedzialne za weryfikowanie poświadczeń żądania i ustawianie użytkownika w kontekście żądania:

    app.UseAuthentication();
    
  • Oprogramowanie Identitypośredniczące serwera, które uwidacznia punkty końcowe openID Połączenie:

    app.UseIdentityServer();
    

usługa aplikacja systemu Azure w systemie Linux

W przypadku wdrożeń usługi aplikacja systemu Azure w systemie Linux określ wystawcę jawnie:

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

W poprzednim kodzie {AUTHORITY} symbol zastępczy jest Authority używany podczas wykonywania wywołań openID Połączenie.

Przykład:

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

AddApiAuthorization

Ta metoda pomocnika konfiguruje Identityserwer do korzystania z naszej obsługiwanej konfiguracji. IdentitySerwer 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 związanych z uwierzytelnianiem pełna moc serwera Identityjest 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 z Identityserwerem z domyślnym zakresem <<ApplicationName>>API i konfiguruje oprogramowanie pośredniczące tokenu elementu nośnego JWT w celu sprawdzania poprawności tokenów wystawionych przez Identityserwer 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ślne zasady autoryzacji mają być skonfigurowane tak, aby używały domyślnego schematu uwierzytelniania, który został skonfigurowany przez AddIdentityServerJwt schemat zasad wymienionych powyżej, dzięki czemu JwtBearerHandler skonfigurowana przez taką metodę pomocnika domyślna procedura obsługi żądań do aplikacji.

ApplicationDbContext

W pliku zwróć uwagę, że to samo DbContext jest używane z Identity wyjątkiem, który rozszerza ApiAuthorizationDbContext (bardziej pochodną klasę z IdentityDbContext) w celu uwzględnienia schematu dla Identityserwera.

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

OidcConfigurationController

W pliku zwróć uwagę na punkt końcowy, który jest aprowizowany w celu 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

appsettings.Development.json W pliku głównym projektu znajduje się IdentityServer sekcja opisujący 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 przepływ wylogowywanie aplikacji.
    • login-menu.component.ts: widżet, który wyświetla jeden z następujących zestawów łączy:
      • Zarządzanie profilami użytkowników i wylogowywanie linków podczas uwierzytelniania użytkownika.
      • Rejestracja i logowanie linków, gdy użytkownik nie jest uwierzytelniony.
  • Strażnik AuthorizeGuard trasy, który można dodać do tras i wymaga uwierzytelnienia użytkownika przed wizytą w trasie.
  • Przechwytownik AuthorizeInterceptor HTTP, który dołącza token dostępu do wychodzących żądań HTTP przeznaczonych dla interfejsu API podczas uwierzytelniania użytkownika.
  • Usługa AuthorizeService , która 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.
  • Moduł Angular, który definiuje trasy skojarzone z częściami uwierzytelniania aplikacji. Uwidacznia składnik menu logowania, przechwytywanie, ochronę i usługę do użycia z pozostałej części 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 przepływ wylogowywanie aplikacji.
    • LoginMenu.js: widżet, który wyświetla jeden z następujących zestawów łączy:
      • Zarządzanie profilami użytkowników i wylogowywanie linków podczas uwierzytelniania użytkownika.
      • Rejestracja i logowanie linków, 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 niższego poziomu procesu uwierzytelniania 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 interfejsu API, skonfiguruj jego JwtBearerOptions wystąpienie:

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 programy obsługi zdarzeń.

Aby dostosować obsługę zdarzenia, zawijaj istniejącą procedurę obsługi zdarzeń z dodatkową logiką zgodnie z potrzebami. 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 program obsługi zdarzeń jest zastępowany implementacją niestandardową. Ta implementacja:

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

Ochrona trasy po stronie klienta (Angular)

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

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ę.

Ochrona trasy 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 zostanie uwierzytelniony.

Uwierzytelnianie żądań interfejsu API (React)

Uwierzytelnianie żądań za pomocą platformy React odbywa się najpierw przez zaimportowanie authService wystąpienia z klasy AuthorizeService. Token dostępu jest pobierany z authService obiektu i jest dołączony do żądania, jak pokazano poniżej. W składnikach 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 });
}

Wdróż w środowisku produkcyjnym

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

  • Baza danych do przechowywania Identity kont użytkowników i Identityudziela serwera.
  • Certyfikat produkcyjny do użycia na potrzeby tokenów podpisywania.
    • 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 podmiotu wyróżniającego certyfikatu.

Przykład: Wdrażanie w usłudze aplikacja systemu Azure

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 warstwy Standardowa lub lepszy podczas konfigurowania aplikacji w witrynie Azure Portal w późniejszym 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 magazyn użytkowników osobistych.
  • Lokalizacja magazynu reprezentuje miejsce ładowania certyfikatu z (CurrentUser lub LocalMachine).
  • Właściwość name certyfikatu odpowiada wyróżniającej podmiotowi certyfikatu.

Aby wdrożyć aplikację w usłudze aplikacja systemu Azure Service, wykonaj kroki opisane w artykule Wdrażanie aplikacji na platformie Azure, w którym wyjaśniono, 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 IdentityAPI opiera się na serwerze z zestawem konwencji, wartości domyślnych i ulepszeń, aby uprościć środowisko dla spAs. Nie trzeba powiedzieć, że pełna moc serwera Identityjest dostępna w tle, jeśli integracje ASP.NET Core nie obejmują 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 w przypadku takich rzeczy, jak zgoda lub federacja. W tych scenariuszach użyj serwera Identityi 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 Identityserwera 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 openidzakresy , profilei 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 z serwerem Identity.
    • Zestaw zakresów obejmuje openidzakresy , profilei 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, który jest hostowany wraz z serwerem Identity.
    • 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 z serwerem Identity.
    • 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 elementów lub Resources.

Skonfiguruj właściwości i post_logout_redirect_uri klientaredirect_uri, 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 (SPA) przy użyciu obsługi autoryzacji interfejsu API. ASP.NET Core Identity do uwierzytelniania i przechowywania użytkowników jest połączony z serwerem Identity do implementowania Połączenie OpenID.

Parametr uwierzytelniania został dodany do szablonów projektów Angular i React , które są podobne do parametru uwierzytelniania w szablonach projektów Aplikacji internetowej (Model-View-Controller) i 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 powłokę poleceń 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 kodzie Microsoft.AspNetCore.ApiAuthorization.IdentityPakiet NuGet serwera . Przykłady umożliwiają skonfigurowanie uwierzytelniania i autoryzacji interfejsu AddApiAuthorization API przy użyciu metod i AddIdentityServerJwt rozszerzeń. 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:

  • Startup.ConfigureServices Wewnątrz metody:

    • Identity z domyślnym interfejsem użytkownika:

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

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Uwierzytelnianie za pomocą dodatkowej AddIdentityServerJwt metody pomocniczej, która konfiguruje aplikację do weryfikowania tokenów JWT utworzonych przez Identityserwer:

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

    • Oprogramowanie pośredniczące uwierzytelniania, które jest odpowiedzialne za weryfikowanie poświadczeń żądania i ustawianie użytkownika w kontekście żądania:

      app.UseAuthentication();
      
    • Oprogramowanie Identitypośredniczące serwera, które uwidacznia punkty końcowe openID Połączenie:

      app.UseIdentityServer();
      

usługa aplikacja systemu Azure w systemie Linux

W przypadku wdrożeń usługi aplikacja systemu Azure w systemie Linux określ wystawcę jawnie w pliku Startup.ConfigureServices:

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

W poprzednim kodzie {AUTHORITY} symbol zastępczy jest Authority używany podczas wykonywania wywołań openID Połączenie.

Przykład:

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

AddApiAuthorization

Ta metoda pomocnika konfiguruje Identityserwer do korzystania z naszej obsługiwanej konfiguracji. IdentitySerwer 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 związanych z uwierzytelnianiem pełna moc serwera Identityjest 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 z Identityserwerem z domyślnym zakresem <<ApplicationName>>API i konfiguruje oprogramowanie pośredniczące tokenu elementu nośnego JWT w celu sprawdzania poprawności tokenów wystawionych przez Identityserwer 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ślne zasady autoryzacji mają być skonfigurowane tak, aby używały domyślnego schematu uwierzytelniania, który został skonfigurowany przez AddIdentityServerJwt schemat zasad wymienionych powyżej, dzięki czemu JwtBearerHandler skonfigurowana przez taką metodę pomocnika domyślna procedura obsługi żądań do aplikacji.

ApplicationDbContext

W pliku zwróć uwagę, że to samo DbContext jest używane z Identity wyjątkiem, który rozszerza ApiAuthorizationDbContext (bardziej pochodną klasę z IdentityDbContext) w celu uwzględnienia schematu dla Identityserwera.

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

OidcConfigurationController

W pliku zwróć uwagę na punkt końcowy, który jest aprowizowany w celu 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

appsettings.Development.json W pliku głównym projektu znajduje się IdentityServer sekcja opisujący 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 przepływ wylogowywanie aplikacji.
    • login-menu.component.ts: widżet, który wyświetla jeden z następujących zestawów łączy:
      • Zarządzanie profilami użytkowników i wylogowywanie linków podczas uwierzytelniania użytkownika.
      • Rejestracja i logowanie linków, gdy użytkownik nie jest uwierzytelniony.
  • Strażnik AuthorizeGuard trasy, który można dodać do tras i wymaga uwierzytelnienia użytkownika przed wizytą w trasie.
  • Przechwytownik AuthorizeInterceptor HTTP, który dołącza token dostępu do wychodzących żądań HTTP przeznaczonych dla interfejsu API podczas uwierzytelniania użytkownika.
  • Usługa AuthorizeService , która 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.
  • Moduł Angular, który definiuje trasy skojarzone z częściami uwierzytelniania aplikacji. Uwidacznia składnik menu logowania, przechwytywanie, ochronę i usługę do użycia z pozostałej części 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 przepływ wylogowywanie aplikacji.
    • LoginMenu.js: widżet, który wyświetla jeden z następujących zestawów łączy:
      • Zarządzanie profilami użytkowników i wylogowywanie linków podczas uwierzytelniania użytkownika.
      • Rejestracja i logowanie linków, 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 niższego poziomu procesu uwierzytelniania 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 interfejsu API, skonfiguruj jego JwtBearerOptions wystąpienie:

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 programy obsługi zdarzeń.

Aby dostosować obsługę zdarzenia, zawijaj istniejącą procedurę obsługi zdarzeń z dodatkową logiką zgodnie z potrzebami. 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 program obsługi zdarzeń jest zastępowany implementacją niestandardową. Ta implementacja:

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

Ochrona trasy po stronie klienta (Angular)

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

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ę.

Ochrona trasy 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 zostanie uwierzytelniony.

Uwierzytelnianie żądań interfejsu API (React)

Uwierzytelnianie żądań za pomocą platformy React odbywa się najpierw przez zaimportowanie authService wystąpienia z klasy AuthorizeService. Token dostępu jest pobierany z authService obiektu i jest dołączony do żądania, jak pokazano poniżej. W składnikach 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 });
}

Wdróż w środowisku produkcyjnym

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

  • Baza danych do przechowywania Identity kont użytkowników i Identityudziela serwera.
  • Certyfikat produkcyjny do użycia na potrzeby tokenów podpisywania.
    • 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 podmiotu wyróżniającego certyfikatu.

Przykład: Wdrażanie w usłudze aplikacja systemu Azure

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 warstwy Standardowa lub lepszy podczas konfigurowania aplikacji w witrynie Azure Portal w późniejszym 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 magazyn użytkowników osobistych.
  • Lokalizacja magazynu reprezentuje miejsce ładowania certyfikatu z (CurrentUser lub LocalMachine).
  • Właściwość name certyfikatu odpowiada wyróżniającej podmiotowi certyfikatu.

Aby wdrożyć aplikację w usłudze aplikacja systemu Azure Service, wykonaj kroki opisane w artykule Wdrażanie aplikacji na platformie Azure, w którym wyjaśniono, 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 IdentityAPI opiera się na serwerze z zestawem konwencji, wartości domyślnych i ulepszeń, aby uprościć środowisko dla spAs. Nie trzeba powiedzieć, że pełna moc serwera Identityjest dostępna w tle, jeśli integracje ASP.NET Core nie obejmują 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 w przypadku takich rzeczy, jak zgoda lub federacja. W tych scenariuszach użyj serwera Identityi 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 Identityserwera 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 openidzakresy , profilei 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 z serwerem Identity.
    • Zestaw zakresów obejmuje openidzakresy , profilei 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, który jest hostowany wraz z serwerem Identity.
    • 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 z serwerem Identity.
    • 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 elementów lub Resources.

Skonfiguruj właściwości i post_logout_redirect_uri klientaredirect_uri, 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