Delen via


Een web-API-back-end voor SPA's beveiligen met Identity

Notitie

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Waarschuwing

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Belangrijk

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-versie van dit artikelvoor de huidige release.

ASP.NET Core Identity biedt API's die verificatie, autorisatie en identiteitsbeheer verwerken. De API's maken het mogelijk om eindpunten van een web-API-back-end te beveiligen met cookie-gebaseerde verificatie. Er is een optie op basis van tokens beschikbaar voor clients die geen cookies kunnen gebruiken, maar in het gebruik hiervan bent u verantwoordelijk voor het beveiligen van de tokens. We raden u aan cookies te gebruiken voor browsertoepassingen, omdat de browser deze standaard automatisch verwerkt zonder ze beschikbaar te maken voor JavaScript.

In dit artikel wordt beschreven hoe u Identity gebruikt om een web-API-back-end te beveiligen voor SPA's, zoals Angular-, React- en Vue-apps. Dezelfde back-end-API's kunnen worden gebruikt om Blazor WebAssembly appste beveiligen.

Voorwaarden

De stappen in dit artikel voegen verificatie en autorisatie toe aan een ASP.NET Core Web API-app die:

  • Is nog niet geconfigureerd voor verificatie.
  • Doelen vanaf net8.0 en hoger.
  • Dit kan een minimale API of controller-API zijn.

In sommige testinstructies in dit artikel wordt gebruikgemaakt van de Swagger UI die is opgenomen in de projectsjabloon. De Swagger-gebruikersinterface is niet vereist voor het gebruik van Identity met een web-API-back-end.

NuGet-pakketten installeren

Installeer de volgende NuGet-pakketten:

Gebruik de in-memory database voor de snelste manier om aan de slag te gaan.

Wijzig de database later in SQLite of SQL Server om gebruikersgegevens op te slaan tussen sessies tijdens het testen of voor productiegebruik. Dit introduceert bepaalde complexiteit vergeleken met in-werkgeheugen, omdat de database moet worden aangemaakt via migraties, zoals te zien is in de EF Core beginnershandleiding.

Installeer deze pakketten met behulp van de NuGet-pakketbeheer in Visual Studio of de dotnet-pakket toevoegen CLI-opdracht.

Een IdentityDbContext maken

Voeg een klasse toe met de naam ApplicationDbContext die wordt overgenomen van 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)
    { }
}

De weergegeven code biedt een speciale constructor waarmee de database voor verschillende omgevingen kan worden geconfigureerd.

Voeg indien nodig een of meer van de volgende using instructies toe bij het toevoegen van de code die in deze stappen wordt weergegeven.

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

De EF Core-context configureren

Zoals eerder vermeld, is de eenvoudigste manier om aan de slag te gaan met het gebruik van de in-memory database. Met in-memory begint elke uitvoering met een nieuwe database en hoeft u geen migraties te gebruiken. Voeg na de aanroep van WebApplication.CreateBuilder(args)de volgende code toe om Identity te configureren voor het gebruik van een in-memory database:

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

Als u gebruikersgegevens tussen sessies wilt opslaan tijdens het testen of voor productiegebruik, wijzigt u de database later in SQLite of SQL Server.

Identity-services toevoegen aan de container

Nadat de aanroep naar WebApplication.CreateBuilder(args), roept u AddAuthorization aan om services toe te voegen aan de container voor afhankelijkheidsinjectie (DI):

builder.Services.AddAuthorization();

Identity API's activeren

Na het gesprek naar WebApplication.CreateBuilder(args), roept u AddIdentityApiEndpoints<TUser>(IServiceCollection) en AddEntityFrameworkStores<TContext>(IdentityBuilder)aan.

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

Cookies en eigen tokens worden standaard geactiveerd. Cookies en tokens worden uitgegeven bij inloggen als de querystringparameter useCookies in het inlogendpoint trueis.

Kaart Identity routes

Voer na de oproep naar builder.Build(), een oproep uit naar MapIdentityApi<TUser>(IEndpointRouteBuilder) om de Identity-eindpunten in kaart te brengen:

app.MapIdentityApi<IdentityUser>();

Geselecteerde eindpunten beveiligen

Als u een eindpunt wilt beveiligen, gebruikt u de RequireAuthorization-extensiemethode in de Map{Method} aanroep waarmee de route wordt gedefinieerd. Bijvoorbeeld:

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();

De methode RequireAuthorization kan ook worden gebruikt voor het volgende:

  • Beveiligde Swagger UI-eindpunten, zoals wordt weergegeven in het volgende voorbeeld:

    app.MapSwagger().RequireAuthorization();
    
  • Beveiligen met een specifieke claim of machtiging, zoals wordt weergegeven in het volgende voorbeeld:

    .RequireAuthorization("Admin");
    

Beveilig eindpunten in een web-API-project op basis van een controller door de [Authorize] toe te passen kenmerk op een controller of actie.

De API testen

Een snelle manier om verificatie te testen is door gebruik te maken van de in-memory database en de Swagger-gebruikersinterface die is opgenomen in de projectsjabloon. De volgende stappen laten zien hoe u de API test met de Swagger-gebruikersinterface. Zorg ervoor dat de Swagger UI-eindpunten niet zijn beveiligd.

Toegang proberen te krijgen tot een beveiligd eindpunt

  • Voer de app uit en navigeer naar de Swagger-gebruikersinterface.
  • Vouw een beveiligd eindpunt uit, zoals /weatherforecast in een project dat is gemaakt met de web-API-sjabloon.
  • Selecteer Probeer het.
  • Selecteer Voer uit. Het antwoord is 401 - not authorized.

Testregistratie

  • Breid /register uit en selecteer Uitproberen.

  • In de sectie Parameters van de gebruikersinterface wordt een voorbeeld van de aanvraagbody weergegeven:

    {
      "email": "string",
      "password": "string"
    }
    
  • Vervang 'tekenreeks' door een geldig e-mailadres en wachtwoord en selecteer vervolgens uitvoeren.

    Als u wilt voldoen aan de standaardregels voor wachtwoordvalidatie, moet het wachtwoord ten minste zes tekens lang zijn en ten minste één van de volgende tekens bevatten:

    • Hoofdletter
    • Kleine letter
    • Cijfer
    • Niet-phanumerisch teken

    Als u een ongeldig e-mailadres of een ongeldig wachtwoord invoert, bevat het resultaat de validatiefouten. Hier volgt een voorbeeld van een antwoordtekst met validatiefouten:

    {
      "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')."
        ]
      }
    }
    

    De fouten worden geretourneerd in de ProblemDetails-indeling, zodat de client deze kan parseren en validatiefouten indien nodig kan weergeven.

    Een geslaagde registratie resulteert in een 200 - OK antwoord.

Aanmelding testen

  • Vouw /login uit en selecteer Uitproberen. In de voorbeeldtekst van de aanvraag ziet u twee extra parameters:

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

    De extra JSON-eigenschappen zijn niet nodig voor dit voorbeeld en kunnen worden verwijderd. Stel useCookies in op true.

  • Vervang 'tekenreeks' door het e-mailadres en wachtwoord dat u hebt gebruikt om te registreren en selecteer vervolgens uitvoeren.

    Een geslaagde aanmelding resulteert in een 200 - OK antwoord met een cookie in de antwoordheader.

Het beveiligde eindpunt opnieuw testen

Voer na een geslaagde aanmelding het beveiligde eindpunt opnieuw uit. De verificatie cookie wordt automatisch verzonden met de aanvraag en het eindpunt is geautoriseerd. Cookie-gebaseerde authenticatie is veilig geïntegreerd in de browser en werkt gewoon.

Testen met niet-browserclients

Sommige webclients bevatten mogelijk niet standaard cookies in de header:

  • Als u een hulpprogramma gebruikt voor het testen van API's, moet u mogelijk cookies inschakelen in de instellingen.

  • De JavaScript fetch-API bevat standaard geen cookies. Schakel ze in door credentials in te stellen op de waarde include in de opties.

  • Voor een HttpClient die in een Blazor WebAssembly-app draait, moet de HttpRequestMessage referenties bevatten zoals het volgende voorbeeld:

    request.SetBrowserRequestCredential(BrowserRequestCredentials.Include);
    

Verificatie op basis van tokens gebruiken

We raden u aan cookies te gebruiken in browsertoepassingen, omdat de browser deze standaard automatisch verwerkt zonder ze beschikbaar te maken voor JavaScript.

Er wordt een aangepast token (een token dat eigendom is van het ASP.NET Core Identity Platform) uitgegeven dat kan worden gebruikt om volgende aanvragen te verifiëren. Het token wordt doorgegeven in de Authorization-header als bearer-token. Er wordt ook een verversingstoken opgegeven. Met dit token kan de toepassing een nieuw token aanvragen wanneer het oude token verloopt zonder dat de gebruiker zich opnieuw hoeft aan te melden.

De tokens zijn geen standaard JSON-webtokens (JWT's). Het gebruik van aangepaste tokens is opzettelijk, omdat de ingebouwde Identity-API voornamelijk bedoeld is voor eenvoudige scenario's. De tokenoptie is niet bedoeld als een volledig functionele id-serviceprovider of tokenserver, maar in plaats daarvan een alternatief voor de cookie optie voor clients die geen cookies kunnen gebruiken.

Als u verificatie op basis van tokens wilt gebruiken, stelt u de useCookies querytekenreeksparameter in op false bij het aanroepen van het /login-eindpunt. Tokens gebruiken het bearer verificatieschema. Als u het token gebruikt dat wordt geretourneerd door de aanroep naar /login, moeten volgende aanroepen naar beveiligde eindpunten de header toevoegen Authorization: Bearer <token> waar <token> het toegangstoken is. Zie Het POST /login-eindpunt gebruiken verderop in dit artikel voor meer informatie.

Uitloggen

Als u een manier wilt bieden om de gebruiker af te melden, definieert u een /logout-eindpunt, zoals in het volgende voorbeeld:

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

Geef een leeg JSON-object ({}) op in de hoofdtekst van de aanvraag bij het aanroepen van dit eindpunt. De volgende code is een voorbeeld van een aanroep naar het afmeldingseindpunt:

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

De MapIdentityApi<TUser>-eindpunten

De aanroep van MapIdentityApi<TUser> voegt de volgende eindpunten toe aan de app:

Het POST /register-eindpunt gebruiken

De hoofdtekst van de aanvraag moet Email- en Password eigenschappen hebben:

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

Zie voor meer informatie:

Het POST /login-eindpunt gebruiken

In de aanvraagbody zijn Email en Password vereist. Als tweeledige verificatie (2FA) is ingeschakeld, is TwoFactorCode of TwoFactorRecoveryCode vereist. Als 2FA niet is ingeschakeld, laat u zowel twoFactorCode als twoFactorRecoveryCodeweg. Voor meer informatie, zie Gebruik de POST /manage/2fa-eindpunt verderop in dit artikel.

Hier volgt een voorbeeld van de aanvraagtekst waarbij 2FA niet is ingeschakeld:

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

Hier volgen voorbeelden van aanvraagteksten waarvoor 2FA is ingeschakeld:

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

Het eindpunt verwacht een queryreeksparameter:

  • useCookies : ingesteld op true voor verificatie op basis van cookie. Ingesteld op false of weglaten voor verificatie op basis van tokens.

Voor meer informatie over op cookiegebaseerde verificatie, zie Test login eerder in dit artikel.

Verificatie op basis van tokens

Als useCookiesfalse is of wordt weggelaten, is authenticatie op basis van tokens ingeschakeld. De hoofdtekst van het antwoord bevat de volgende eigenschappen:

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

Zie AccessTokenResponsevoor meer informatie over deze eigenschappen.

Plaats het toegangstoken in een header om geverifieerde aanvragen te maken, zoals wordt weergegeven in het volgende voorbeeld

Authorization: Bearer {access token}

Wanneer het toegangstoken bijna verloopt, roept u het /refresh-eindpunt aan.

Het POST /refresh-eindpunt gebruiken

Alleen voor gebruik met verificatie op basis van tokens. Hiermee haalt u een nieuw toegangstoken op zonder dat de gebruiker zich opnieuw moet aanmelden. Roep dit API-eindpunt aan wanneer het toegangstoken op het punt staat te verlopen.

De hoofdtekst van de aanvraag bevat alleen de RefreshToken. Hier volgt een voorbeeld van de aanvraagtekst:

{
  "refreshToken": "string"
}

Als de aanroep succesvol is, is de antwoordtekst een nieuwe AccessTokenResponse, zoals in het volgende voorbeeld wordt getoond.

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

Het GET /confirmEmail-eindpunt gebruiken

Als Identity is ingesteld voor e-mailbevestiging, stuurt een geslaagde aanroep naar het /register-eindpunt een e-mail met een koppeling naar het /confirmEmail-eindpunt. De koppeling bevat de volgende queryreeksparameters:

  • userId
  • code
  • changedEmail - Alleen opgenomen als de gebruiker het e-mailadres tijdens de registratie heeft gewijzigd.

Identity bevat standaardtekst voor de bevestigingsmail. Het onderwerp van het e-mailbericht is standaard 'Bevestig uw e-mail' en de hoofdtekst van de e-mail ziet er als volgt uit:

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

Als de eigenschap RequireConfirmedEmail is ingesteld op true, kan de gebruiker zich pas aanmelden nadat het e-mailadres is bevestigd door op de koppeling in het e-mailbericht te klikken. Het /confirmEmail-eindpunt:

  • Bevestigt het e-mailadres en stelt de gebruiker in staat zich aan te melden.
  • Retourneert de tekst 'Bedankt voor het bevestigen van uw e-mail'. In de hoofdtekst van het antwoord.

Als u Identity wilt instellen voor e-mailbevestiging, voegt u code toe in Program.cs om RequireConfirmedEmail in te stellen op true en voegt u een klasse toe waarmee IEmailSender wordt geïmplementeerd in de DI-container. Bijvoorbeeld:

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

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

Zie accountbevestiging en wachtwoordherstel in ASP.NET Corevoor meer informatie.

Identity bevat standaardtekst voor de andere e-mailberichten die ook moeten worden verzonden, zoals voor 2FA en wachtwoordherstel. Als u deze e-mailberichten wilt aanpassen, geeft u een aangepaste implementatie van de IEmailSender-interface op. In het voorgaande voorbeeld is EmailSender een klasse die IEmailSenderimplementeert. Zie voor meer informatie, waaronder een voorbeeld van een klasse die IEmailSenderimplementeert, accountbevestiging en wachtwoordherstel in ASP.NET Core.

Het POST /resendConfirmationEmail-eindpunt gebruiken

Hiermee wordt alleen een e-mailbericht verzonden als het adres geldig is voor een geregistreerde gebruiker.

De hoofdtekst van de aanvraag bevat alleen de Email. Hier volgt een voorbeeld van de aanvraagtekst:

{
  "email": "string"
}

Voor meer informatie, zie Gebruik het GET /confirmEmail-eindpunt eerder in dit artikel.

Het POST /forgotPassword-eindpunt gebruiken

Hiermee wordt een e-mailbericht gegenereerd dat een code voor het opnieuw instellen van een wachtwoord bevat. Verzend die code naar /resetPassword met een nieuw wachtwoord.

De hoofdtekst van de aanvraag bevat alleen de Email. Hier volgt een voorbeeld:

{
  "email": "string"
}

Voor informatie over het inschakelen van Identity om e-mailberichten te verzenden, zie Het GET /confirmEmail-eindpunt gebruiken.

Het POST /resetPassword-eindpunt gebruiken

Roep dit eindpunt aan na het ophalen van een resetcode door het /forgotPassword-eindpunt aan te roepen.

De aanvraagbody vereist Email, ResetCodeen NewPassword. Hier volgt een voorbeeld:

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

Het POST /manage/2fa-eindpunt gebruiken

Hiermee configureert u tweeledige verificatie (2FA) voor de gebruiker. Wanneer 2FA is ingeschakeld, is voor geslaagde aanmelding een code vereist die wordt geproduceerd door een authenticator-app, naast het e-mailadres en wachtwoord.

2FA inschakelen

2FA inschakelen voor de momenteel geverifieerde gebruiker:

  • Roep het /manage/2fa-eindpunt aan en verzend een leeg JSON-object ({}) in de hoofdtekst van de aanvraag.

  • De hoofdtekst van het antwoord bevat de SharedKey samen met een aantal andere eigenschappen die op dit moment niet nodig zijn. De gedeelde sleutel wordt gebruikt om de authenticator-app in te stellen. Voorbeeld van antwoordtekst:

    {
      "sharedKey": "string",
      "recoveryCodesLeft": 0,
      "recoveryCodes": null,
      "isTwoFactorEnabled": false,
      "isMachineRemembered": false
    }
    
  • Gebruik de gedeelde sleutel om een tijdgebonden eenmalig wachtwoord (TOTP) op te halen. Zie Het genereren van QR-code inschakelen voor TOTP authenticator-apps in ASP.NET Corevoor meer informatie.

  • Roep het /manage/2fa-eindpunt aan en verzend de TOTP en "enable": true in de body van het verzoek. Bijvoorbeeld:

    {
      "enable": true,
      "twoFactorCode": "string"
    }
    
  • De hoofdtekst van het antwoord bevestigt dat IsTwoFactorEnabled waar is en bevat de RecoveryCodes. De herstelcodes worden gebruikt om u aan te melden wanneer de authenticator-app niet beschikbaar is. Voorbeeld van antwoordtekst na het inschakelen van 2FA:

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

Aanmelden met 2FA

Benader het /login-eindpunt door het e-mailadres, wachtwoord en TOTP mee te sturen in de aanvraagbody. Bijvoorbeeld:

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

Als de gebruiker geen toegang heeft tot de authenticator-app, meldt u zich aan door het /login eindpunt aan te roepen met een van de herstelcodes die zijn opgegeven toen 2FA werd ingeschakeld. De hoofdtekst van de aanvraag ziet er als volgt uit:

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

Herstelcodes opnieuw instellen

Als u een nieuwe verzameling herstelcodes wilt ophalen, roept u dit eindpunt aan met ResetRecoveryCodes ingesteld op true. Hier volgt een voorbeeld van de aanvraagtekst:

{
  "resetRecoveryCodes": true
}

De gedeelde sleutel opnieuw instellen

Als u een nieuwe willekeurige gedeelde sleutel wilt ophalen, roept u dit eindpunt aan met ResetSharedKey ingesteld op true. Hier volgt een voorbeeld van de aanvraagtekst:

{
  "resetSharedKey": true
}

Als u de sleutel opnieuw wilt instellen, wordt de tweeledige aanmeldingsvereiste voor de geverifieerde gebruiker automatisch uitgeschakeld totdat deze opnieuw wordt ingeschakeld door een latere aanvraag.

Vergeet de machine

Als u de cookie 'onthoud-mij-vlag' wilt wissen als deze aanwezig is, roept u dit eindpunt aan met ForgetMachine ingesteld op true. Hier volgt een voorbeeld van de aanvraagtekst:

{
  "forgetMachine": true
}

Dit eindpunt heeft geen invloed op verificatie op basis van tokens.

Het GET /manage/info-eindpunt gebruiken

Hiermee haalt u het e-mailadres en de e-mailbevestigingsstatus van de aangemelde gebruiker op. Claims zijn om veiligheidsredenen weggelaten vanuit dit eindpunt. Als claims nodig zijn, gebruikt u de API's aan de serverzijde om een eindpunt voor claims in te stellen. Of in plaats van alle claims van de gebruikers te delen, geeft u een validatie-eindpunt op dat een claim accepteert en reageert of de gebruiker deze heeft.

Voor de aanvraag zijn geen parameters vereist. De hoofdtekst van het antwoord bevat de eigenschappen Email en IsEmailConfirmed, zoals in het volgende voorbeeld:

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

Het POST /manage/info-eindpunt gebruiken

Hiermee werkt u het e-mailadres en wachtwoord van de aangemelde gebruiker bij. Verzend NewEmail, NewPassworden OldPassword in de aanvraagbody, zoals wordt weergegeven in het volgende voorbeeld:

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

Hier volgt een voorbeeld van de hoofdtekst van het antwoord:

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

Zie ook

Zie de volgende bronnen voor meer informatie:

De ASP.NET Core-sjablonen bieden verificatie in SPA's (Single Page Apps) met behulp van de ondersteuning voor API-autorisatie. ASP.NET Core-Identity voor het verifiëren en opslaan van gebruikers wordt gecombineerd met Duende Identity Server- voor het implementeren van OpenID Connect.

Belangrijk

Duende Software moet u mogelijk een licentiekosten betalen voor productiegebruik van Duende Identity Server. Zie Migreren van ASP.NET Core 5.0 naar 6.0voor meer informatie.

Er is een authenticatieparameter toegevoegd aan de projectsjablonen van Angular en React die vergelijkbaar is met de authenticatieparameter in de projectsjablonen van Web Application (Model-View-Controller) en Web Application (Razor Pages). De toegestane parameterwaarden zijn Geen en Individueel. De projectsjabloon React.js en Redux biedt momenteel geen ondersteuning voor de verificatieparameter.

Een app maken met API-autorisatieondersteuning

Gebruikersverificatie en autorisatie kunnen worden gebruikt met zowel Angular als React-SPA's. Open een opdrachtshell en voer de volgende opdracht uit:

Angular-:

dotnet new angular -au Individual

React:

dotnet new react -au Individual

Met de voorgaande opdracht maakt u een ASP.NET Core-app met een ClientApp map die de SPA bevat.

Algemene beschrijving van de ASP.NET Kernonderdelen van de app

In de volgende secties worden toevoegingen aan het project beschreven wanneer ondersteuning voor verificatie is opgenomen:

Program.cs

De volgende codevoorbeelden zijn afhankelijk van het Microsoft.AspNetCore.ApiAuthorization.IdentityServer NuGet-pakket. In de voorbeelden worden API-verificatie en -autorisatie geconfigureerd met behulp van de AddApiAuthorization- en AddIdentityServerJwt-extensiemethoden. Projecten die gebruikmaken van de projectsjablonen React of Angular SPA met verificatie, bevatten een verwijzing naar dit pakket.

dotnet new angular -au Individual genereert het volgende Program.cs bestand:

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();

Met de voorgaande code wordt het volgende geconfigureerd:

  • Identity met de standaardgebruikersinterface:

    builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlite(connectionString));
    builder.Services.AddDatabaseDeveloperPageExceptionFilter();
    
    builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    
  • IdentityServer met een extra AddApiAuthorization helpermethode waarmee enkele standaard-ASP.NET Core-conventies bovenop IdentityServer worden ingesteld:

    builder.Services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
    
  • Verificatie met een extra AddIdentityServerJwt helpermethode waarmee de app wordt geconfigureerd voor het valideren van JWT-tokens die worden geproduceerd door IdentityServer:

    builder.Services.AddAuthentication()
    .AddIdentityServerJwt();
    
  • De verificatie-middleware die verantwoordelijk is voor het valideren van de aanvraagreferenties en het instellen van de gebruiker in de aanvraagcontext:

    app.UseAuthentication();
    
  • De IdentityServer-middleware waarmee de OpenID Connect-eindpunten worden weergegeven:

    app.UseIdentityServer();
    

Waarschuwing

In dit artikel wordt het gebruik van verbindingsreeksen beschreven. Met een lokale database hoeft de gebruiker niet te worden geverifieerd, maar in productie bevatten verbindingsreeksen soms een wachtwoord om te verifiëren. Een wachtwoordreferentie voor een resource-eigenaar (ROPC) is een beveiligingsrisico dat moet worden vermeden in productiedatabases. Productie-apps moeten gebruikmaken van de veiligste verificatiestroom die beschikbaar is. Zie Beveiligde verificatiestromenvoor meer informatie over verificatie voor apps die zijn geïmplementeerd voor test- of productieomgevingen.

Azure App Service in Linux

Geef voor Azure App Service-implementaties in Linux de verlener expliciet op:

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

In de voorgaande code is de tijdelijke aanduiding {AUTHORITY} de Authority die gebruikt moet worden bij OpenID Connect-aanroepen.

Voorbeeld:

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

AddApiAuthorization

Met deze helpermethode configureert u IdentityServer voor het gebruik van onze ondersteunde configuratie. IdentityServer is een krachtig en uitbreidbaar framework voor het afhandelen van problemen met app-beveiliging. Tegelijkertijd maakt dit onnodige complexiteit bloot voor de meest voorkomende scenario's. Daarom wordt u een set conventies en configuratieopties aangeboden die als een goed uitgangspunt worden beschouwd. Zodra uw verificatiebehoeften zijn gewijzigd, is de volledige kracht van IdentityServer nog steeds beschikbaar om verificatie aan te passen aan uw behoeften.

AddIdentityServerJwt

Met deze helpermethode configureert u een beleidsschema voor de app als de standaardverificatiehandler. Het beleid is zo geconfigureerd dat Identity alle aanvragen die worden doorgestuurd naar een subpad in de Identity URL-ruimte '/Identity' kan verwerken. De JwtBearerHandler verwerkt alle andere aanvragen. Daarnaast registreert deze methode een <<ApplicationName>>API API-resource met IdentityServer met een standaardbereik van <<ApplicationName>>API en configureert u de JWT Bearer-token-middleware om tokens te valideren die zijn uitgegeven door IdentityServer voor de app.

WeatherForecastController

Let in het bestand op het [Authorize] kenmerk dat is toegepast op de klasse die aangeeft dat de gebruiker moet worden geautoriseerd op basis van het standaardbeleid voor toegang tot de resource. Het standaardautorisatiebeleid blijkt zo te zijn geconfigureerd dat het het standaardverificatieschema gebruikt, dat door AddIdentityServerJwt is ingesteld op het eerder genoemde beleidsschema. Dit maakt van de door een dergelijke helpermethode geconfigureerde JwtBearerHandler de standaardhandler voor verzoeken aan de app.

ApplicationDbContext

In het bestand ziet u dat dezelfde DbContext wordt gebruikt in Identity met uitzondering dat het ApiAuthorizationDbContext (een meer afgeleide klasse van IdentityDbContext) uitbreidt om het schema voor IdentityServer op te nemen.

Als u het databaseschema volledig wilt beheren, neemt u het over van een van de beschikbare IdentityDbContext klassen en configureert u de context om het Identity schema op te nemen door builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) aan te roepen op de OnModelCreating methode.

OidcConfigurationController

In het bestand ziet u het eindpunt dat is ingericht voor de OIDC-parameters die de client moet gebruiken.

appsettings.json

In het appsettings.json-bestand van de hoofdmap van het project is er een nieuwe IdentityServer sectie waarin de lijst met geconfigureerde clients wordt beschreven. In het volgende voorbeeld is er één client. De clientnaam komt overeen met de naam van de app en wordt toegewezen aan de parameter OAuth ClientId. Het profiel geeft aan welk app-type er wordt geconfigureerd. Het wordt intern gebruikt om conventies te stimuleren die het configuratieproces voor de server vereenvoudigen. Er zijn verschillende profielen beschikbaar, zoals wordt uitgelegd in de sectie Toepassingsprofielen.

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

appsettings.Development.json

In het appsettings.Development.json-bestand van de hoofdmap van het project bevindt zich een IdentityServer sectie waarin de sleutel wordt beschreven die wordt gebruikt voor het ondertekenen van tokens. Bij de implementatie in productie moet naast de app een sleutel worden ingericht en geïmplementeerd, zoals wordt uitgelegd in de sectie Implementeren in productie.

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

Algemene beschrijving van de Angular-app

De ondersteuning voor verificatie en API-autorisatie in de Angular-sjabloon bevindt zich in een eigen Angular-module in de map ClientApp/src/api-authorization. De module bestaat uit de volgende elementen:

  • 3 onderdelen:
    • login.component.ts: verwerkt de aanmeldingsstroom van de app.
    • logout.component.ts: Verwerkt het uitlogproces van de app.
    • login-menu.component.ts: een widget met een van de volgende sets koppelingen:
      • Beheer van gebruikersprofielen en afmeldingskoppelingen wanneer de gebruiker wordt geverifieerd.
      • Registratie- en inlogkoppelingen wanneer de gebruiker niet is geauthenticeerd.
  • Een routebeveiliging AuthorizeGuard die kan worden toegevoegd aan routes en vereist dat een gebruiker wordt geverifieerd voordat de route wordt bezocht.
  • Een HTTP-interceptor AuthorizeInterceptor die het toegangstoken koppelt aan uitgaande HTTP-aanvragen die gericht zijn op de API wanneer de gebruiker wordt geverifieerd.
  • Een service AuthorizeService die de details op lager niveau van het verificatieproces verwerkt en informatie over de geverifieerde gebruiker beschikbaar maakt voor de rest van de app voor verbruik.
  • Een Angular-module die routes definieert die zijn gekoppeld aan de verificatieonderdelen van de app. Het stelt het aanmeldingsmenucomponent, de interceptor, de bescherming en de service beschikbaar voor gebruik door de rest van de app.

Algemene beschrijving van de React-app

De ondersteuning voor verificatie en API-autorisatie in de React-sjabloon bevindt zich in de map ClientApp/src/components/api- authorization. Deze bestaat uit de volgende elementen:

  • 4 onderdelen:
    • Login.js: verwerkt de aanmeldingsstroom van de app.
    • Logout.js: Behandelt het afmeldingsproces van de app.
    • LoginMenu.js: een widget met een van de volgende sets koppelingen:
      • Beheer van gebruikersprofielen en afmeldingskoppelingen wanneer de gebruiker wordt geverifieerd.
      • Registratie- en inlogkoppelingen wanneer de gebruiker niet is aangemeld.
    • AuthorizeRoute.js: een routeonderdeel waarvoor een gebruiker moet worden geverifieerd voordat het onderdeel wordt weergegeven dat is aangegeven in de parameter Component.
  • Een geëxporteerd authService exemplaar van klasse AuthorizeService die de details van het verificatieproces op lager niveau verwerkt en informatie over de geverifieerde gebruiker beschikbaar maakt voor de rest van de app voor verbruik.

Nu u de belangrijkste onderdelen van de oplossing hebt gezien, kunt u dieper ingaan op afzonderlijke scenario's voor de app.

Autorisatie vereisen voor een nieuwe API

Het systeem is standaard geconfigureerd om eenvoudig autorisatie voor nieuwe API's te vereisen. Hiervoor maakt u een nieuwe controller en voegt u het kenmerk [Authorize] toe aan de controllerklasse of aan een actie binnen de controller.

De API-verificatiehandler aanpassen

Configureer de JwtBearerOptions instantie om de configuratie van de JWT-handler van de API aan te passen:

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

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

De JWT-handler van de API genereert gebeurtenissen die controle over het verificatieproces mogelijk maken met behulp van JwtBearerEvents. Voor ondersteuning voor API-autorisatie registreert AddIdentityServerJwt zijn eigen gebeurtenis-handlers.

Als u de verwerking van een gebeurtenis wilt aanpassen, verpakt u de bestaande gebeurtenis-handler met aanvullende logica zoals vereist. Bijvoorbeeld:

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

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

In de voorgaande code wordt de OnTokenValidated gebeurtenis-handler vervangen door een aangepaste implementatie. Deze implementatie:

  1. Roept de oorspronkelijke implementatie aan die wordt geleverd door de API-autorisatieondersteuning.
  2. Voer een eigen aangepaste logica uit.

Een route aan de clientzijde beveiligen (Angular)

Het beveiligen van een route aan de clientzijde wordt uitgevoerd door de autorisatiebeveiliging toe te voegen aan de lijst met beveiligingsmaatregelen die moeten worden uitgevoerd bij het configureren van een route. U kunt bijvoorbeeld zien hoe de fetch-data route is geconfigureerd in de Angular-module van de hoofd-app:

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

Het is belangrijk te vermelden dat het beveiligen van een route niet het werkelijke eindpunt beveiligt (waarvoor nog steeds een [Authorize] kenmerk is toegepast), maar dat de gebruiker alleen niet naar de opgegeven route aan de clientzijde navigeert wanneer het niet is geverifieerd.

API-aanvragen verifiëren (Angular)

Het verifiëren van aanvragen voor API's die naast de app worden gehost, wordt automatisch uitgevoerd via het gebruik van de HTTP-client-interceptor die is gedefinieerd door de app.

Een route aan de clientzijde beveiligen (React)

Beveilig een clientside-route door de AuthorizeRoute-component te gebruiken in plaats van de Route-component. U ziet bijvoorbeeld hoe de fetch-data route is geconfigureerd in het App-onderdeel:

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

Een route beveiligen:

  • Beveiligt het werkelijke eindpunt niet (waarvoor nog steeds een [Authorize] kenmerk is vereist dat erop wordt toegepast).
  • Hiermee voorkomt u alleen dat de gebruiker naar de opgegeven route aan de clientzijde navigeert wanneer deze niet is geverifieerd.

API-aanvragen verifiëren (React)

Het verifiëren van aanvragen met React wordt uitgevoerd door eerst het authService exemplaar uit de AuthorizeServicete importeren. Het toegangstoken wordt opgehaald uit de authService en is gekoppeld aan de aanvraag, zoals hieronder wordt weergegeven. In React-onderdelen wordt dit werk doorgaans uitgevoerd in de componentDidMount levenscyclusmethode of als gevolg van een bepaalde gebruikersinteractie.

Importeer authService in een onderdeel

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

Het toegangstoken ophalen en koppelen aan het antwoord

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 });
}

Uitrollen naar productie

Als u de app wilt implementeren in productie, moeten de volgende resources worden ingericht:

  • Een database om de Identity gebruikersaccounts en de gebruikersrechten van de IdentityServer op te slaan.
  • Een productiecertificaat om te gebruiken voor het ondertekenen van tokens.
    • Er zijn geen specifieke vereisten voor dit certificaat; het kan een zelfondertekend certificaat zijn of een certificaat dat is ingericht via een CA-instantie.
    • Het kan worden gegenereerd via standaardhulpprogramma's zoals PowerShell of OpenSSL.
    • Het kan worden geïnstalleerd in het certificaatarchief op de doelcomputers of worden geïmplementeerd als een PFX--bestand met een sterk wachtwoord.

Voorbeeld: Implementeren naar een niet-Azure-webhostingprovider

Maak of laad uw certificaat in het webhostingvenster. Wijzig vervolgens in het appsettings.json-bestand van de app de sectie IdentityServer om de belangrijkste details op te nemen. Bijvoorbeeld:

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

In het voorgaande voorbeeld:

  • StoreName vertegenwoordigt de naam van het certificaatarchief waarin het certificaat is opgeslagen. In dit geval verwijst het naar de webhostingwinkel.
  • StoreLocation geeft aan waar het certificaat moet worden geladen (CurrentUser in dit geval).
  • Name komt overeen met het onderscheiden onderwerp voor het certificaat.

Voorbeeld: Implementeren in Azure App Service

In deze sectie wordt beschreven hoe u de app implementeert in Azure App Service met behulp van een certificaat dat is opgeslagen in het certificaatarchief. Als u de app wilt wijzigen om een certificaat uit het certificaatarchief te laden, is een serviceplan voor de Standard-laag of beter vereist wanneer u de app in Azure Portal in een latere stap configureert.

Wijzig in het appsettings.json-bestand van de app de sectie IdentityServer om de belangrijkste details op te nemen:

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "My",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}
  • De winkelnaam vertegenwoordigt de naam van het certificaatarchief waarin het certificaat is opgeslagen. In dit geval verwijst het naar de persoonlijke gebruikersopslag.
  • De opslaglocatie geeft aan waar het certificaat moet worden geladen (CurrentUser of LocalMachine).
  • De naamseigenschap op het certificaat komt overeen met het onderscheidend onderwerp voor het certificaat.

Als u naar Azure App Service wilt implementeren, volgt u de stappen in De app implementeren in Azure, waarin wordt uitgelegd hoe u de benodigde Azure-resources maakt en de app implementeert in productie.

Nadat u de voorgaande instructies hebt gevolgd, wordt de app geïmplementeerd in Azure, maar is deze nog niet functioneel. Het certificaat dat door de app wordt gebruikt, moet worden geconfigureerd in Azure Portal. Zoek de vingerafdruk voor het certificaat en volg de stappen beschreven in Laad uw certificaten.

Hoewel in deze stappen SSL wordt vermeld, is er een sectie privécertificaten in Azure Portal waar u het ingerichte certificaat kunt uploaden voor gebruik met de app.

Nadat u de app en de instellingen van de app in Azure Portal hebt geconfigureerd, start u de app opnieuw op in de portal.

Andere configuratieopties

De ondersteuning voor API-autorisatie bouwt voort op IdentityServer met een set conventies, standaardwaarden en verbeteringen om de ervaring voor SPA's te vereenvoudigen. Onnodig te zeggen, de volledige kracht van IdentityServer is achter de schermen beschikbaar als de ASP.NET Core-integraties uw scenario niet dekken. De ASP.NET Core-ondersteuning is gericht op 'first party'-apps, waar alle apps door onze organisatie worden gemaakt en geïmplementeerd. Daarom wordt ondersteuning niet aangeboden voor zaken zoals toestemming of federatie. Voor deze scenario's gebruikt u IdentityServer en volgt u de bijbehorende documentatie.

Toepassingsprofielen

Toepassingsprofielen zijn vooraf gedefinieerde configuraties voor apps die hun parameters verder definiëren. Op dit moment worden de volgende profielen ondersteund:

  • IdentityServerSPA: Vertegenwoordigt een SPA die naast IdentityServer als één eenheid wordt gehost.
    • De redirect_uri wordt standaard ingesteld op /authentication/login-callback.
    • De post_logout_redirect_uri wordt standaard ingesteld op /authentication/logout-callback.
    • De set van bereik omvat de openid, profileen elk gedefinieerd bereik voor de API's in de app.
    • De set toegestane OIDC-antwoordtypen is id_token token of elk afzonderlijk (id_token, token).
    • De toegestane antwoordmodus is fragment.
  • SPA: Staat voor een SPA die niet wordt gehost door IdentityServer.
    • De reeks van bereiken omvat de openid, profileen elk bereik dat is gedefinieerd voor de API's in de app.
    • De set toegestane OIDC-antwoordtypen is id_token token of elk afzonderlijk (id_token, token).
    • De toegestane antwoordmodus is fragment.
  • IdentityServerJwt: Vertegenwoordigt een API die naast IdentityServer wordt gehost.
    • De app is geconfigureerd voor één bereik dat standaard de naam van de app heeft.
  • API: Vertegenwoordigt een API die niet wordt gehost met IdentityServer.
    • De app is geconfigureerd voor één bereik dat standaard de naam van de app heeft.

Configuratie via AppSettings

Configureer de apps via het configuratiesysteem door ze toe te voegen aan de lijst met Clients of Resources.

Configureer de eigenschap redirect_uri en post_logout_redirect_uri van elke client, zoals wordt weergegeven in het volgende voorbeeld:

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

Wanneer u bronnen configureert, kunt u de scopes voor de bron instellen zoals hieronder weergegeven:

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

Configuratie via code

U kunt de clients en resources ook configureren via code, met behulp van een overload van AddApiAuthorization die een actie gebruikt om opties te configureren.

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"));
});

Aanvullende informatiebronnen

De sjablonen ASP.NET Core 3.1 en hoger bieden verificatie in SPA's (Single Page Apps) met behulp van de ondersteuning voor API-autorisatie. ASP.NET Core Identity voor het verifiëren en opslaan van gebruikers wordt gecombineerd met IdentityServer- voor het implementeren van OpenID Connect.

Er is een verificatieparameter toegevoegd aan de Angular en React projectsjablonen; deze is vergelijkbaar met de verificatieparameter in de webapplicatie (Model-View-Controller) en webapplicatie (Razor Pages) projectsjablonen. De toegestane parameterwaarden zijn Geen en Afzonderlijke. De projectsjabloon React.js en Redux biedt momenteel geen ondersteuning voor de verificatieparameter.

Een app maken met API-autorisatieondersteuning

Gebruikersverificatie en autorisatie kunnen worden gebruikt met zowel Angular als React-SPA's. Open een opdrachtshell en voer de volgende opdracht uit:

Angular-:

dotnet new angular -o <output_directory_name> 

React:

dotnet new react -o <output_directory_name> -au Individual

Met de voorgaande opdracht maakt u een ASP.NET Core-app met een ClientApp map die de SPA bevat.

Algemene beschrijving van de ASP.NET Kernonderdelen van de app

In de volgende secties worden toevoegingen aan het project beschreven wanneer ondersteuning voor verificatie is opgenomen:

Startup klasse

De volgende codevoorbeelden zijn afhankelijk van het Microsoft.AspNetCore.ApiAuthorization.IdentityServer NuGet-pakket. In de voorbeelden worden API-verificatie en -autorisatie geconfigureerd met behulp van de AddApiAuthorization- en AddIdentityServerJwt-extensiemethoden. Projecten die gebruikmaken van de projectsjablonen React of Angular SPA met verificatie, bevatten een verwijzing naar dit pakket.

De klasse Startup heeft de volgende toevoegingen:

  • In de methode Startup.ConfigureServices:

    • Identity met de standaardgebruikersinterface:

      services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
      
      services.AddDefaultIdentity<ApplicationUser>()
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • IdentityServer met een extra AddApiAuthorization helpermethode waarmee enkele standaard-ASP.NET Core-conventies bovenop IdentityServer worden ingesteld:

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Verificatie met een extra AddIdentityServerJwt helpermethode waarmee de app wordt geconfigureerd voor het valideren van JWT-tokens die worden geproduceerd door IdentityServer:

      services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • Binnen de methode Startup.Configure:

    • De verificatie-middleware die verantwoordelijk is voor het valideren van de aanvraagreferenties en het instellen van de gebruiker in de aanvraagcontext:

      app.UseAuthentication();
      
    • De IdentityServer-middleware waarmee de OpenID Connect-eindpunten worden weergegeven:

      app.UseIdentityServer();
      

Waarschuwing

In dit artikel wordt het gebruik van verbindingsreeksen beschreven. Met een lokale database hoeft de gebruiker niet te worden geverifieerd, maar in productie bevatten verbindingsreeksen soms een wachtwoord om te verifiëren. Inloggegevens voor een wachtwoord van een resource-eigenaar (ROPC) vormen een beveiligingsrisico dat moet worden vermeden in productiedatabases. Productie-apps moeten gebruikmaken van de veiligste verificatiestroom die beschikbaar is. Zie Beveiligde verificatiestromenvoor meer informatie over verificatie voor apps die zijn geïmplementeerd voor test- of productieomgevingen.

Azure App Service in Linux

Geef voor Azure App Service-implementaties in Linux de verlener expliciet op in Startup.ConfigureServices:

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

In de voorgaande coderegel wordt de tijdelijke aanduiding {AUTHORITY} gebruikt als de Authority bij het uitvoeren van OpenID Connect-aanroepen.

Voorbeeld:

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

AddApiAuthorization

Met deze helpermethode configureert u IdentityServer voor het gebruik van onze ondersteunde configuratie. IdentityServer is een krachtig en uitbreidbaar framework voor het afhandelen van problemen met app-beveiliging. Tegelijkertijd maakt dit onnodige complexiteit bloot voor de meest voorkomende scenario's. Daarom wordt u een set conventies en configuratieopties aangeboden die als een goed uitgangspunt worden beschouwd. Zodra uw verificatiebehoeften zijn gewijzigd, is de volledige kracht van IdentityServer nog steeds beschikbaar om verificatie aan te passen aan uw behoeften.

AddIdentityServerJwt

Met deze helpermethode configureert u een beleidsschema voor de app als de standaardverificatiehandler. Het beleid is zo geconfigureerd dat Identity alle aanvragen die worden doorgestuurd naar een subpad in de Identity URL-ruimte '/Identity' kan verwerken. De JwtBearerHandler verwerkt alle andere aanvragen. Daarnaast registreert deze methode een <<ApplicationName>>API API-resource met IdentityServer met een standaardbereik van <<ApplicationName>>API en configureert u de JWT Bearer-token-middleware om tokens te valideren die zijn uitgegeven door IdentityServer voor de app.

WeatherForecastController

Let in het bestand op het [Authorize] kenmerk dat is toegepast op de klasse die aangeeft dat de gebruiker moet worden geautoriseerd op basis van het standaardbeleid voor toegang tot de resource. Het standaardautorisatiebeleid wordt geconfigureerd voor het gebruik van het standaardauthenticatieschema, dat door AddIdentityServerJwt is ingesteld op het hierboven genoemde beleidsschema, waardoor JwtBearerHandler, geconfigureerd door dergelijke helpermethoden, de standaardhandler wordt voor verzoeken aan de app.

ApplicationDbContext

In het bestand ziet u dat dezelfde DbContext wordt gebruikt in Identity met uitzondering dat het ApiAuthorizationDbContext (een meer afgeleide klasse van IdentityDbContext) uitbreidt om het schema voor IdentityServer op te nemen.

Als u het databaseschema volledig wilt beheren, neemt u het over van een van de beschikbare IdentityDbContext klassen en configureert u de context om het Identity schema op te nemen door builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) aan te roepen op de OnModelCreating methode.

OidcConfigurationController

In het bestand ziet u het eindpunt dat is ingericht voor de OIDC-parameters die de client moet gebruiken.

appsettings.json

In het appsettings.json-bestand van de hoofdmap van het project is er een nieuwe IdentityServer sectie waarin de lijst met geconfigureerde clients wordt beschreven. In het volgende voorbeeld is er één client. De clientnaam komt overeen met de naam van de app en wordt toegewezen aan de parameter OAuth ClientId. Het profiel geeft aan welk type app wordt geconfigureerd. Het wordt intern gebruikt om conventies te stimuleren die het configuratieproces voor de server vereenvoudigen. Er zijn verschillende profielen beschikbaar, zoals wordt uitgelegd in de sectie Toepassingsprofielen.

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

appsettings.Development.json

In het appsettings.Development.json-bestand van de hoofdmap van het project bevindt zich een IdentityServer sectie waarin de sleutel wordt beschreven die wordt gebruikt voor het ondertekenen van tokens. Bij de implementatie in productie moet naast de app een sleutel worden ingericht en geïmplementeerd, zoals wordt uitgelegd in de sectie Implementeren in productie.

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

Algemene beschrijving van de Angular-app

De ondersteuning voor verificatie en API-autorisatie in de Angular-sjabloon bevindt zich in een eigen Angular-module in de map ClientApp/src/api-authorization. De module bestaat uit de volgende elementen:

  • 3 onderdelen:
    • login.component.ts: verwerkt de aanmeldingsstroom van de app.
    • logout.component.ts: Behandelt het uitlogproces van de app.
    • login-menu.component.ts: een widget met een van de volgende sets koppelingen:
      • Beheer van gebruikersprofielen en afmeldingskoppelingen wanneer de gebruiker wordt geverifieerd.
      • Registratie- en aanmeldingskoppelingen wanneer de gebruiker niet is geverifieerd.
  • Een routebeveiliging AuthorizeGuard die kan worden toegevoegd aan routes en vereist dat een gebruiker wordt geverifieerd voordat de route wordt bezocht.
  • Een HTTP-interceptor AuthorizeInterceptor die het toegangstoken koppelt aan uitgaande HTTP-aanvragen die gericht zijn op de API wanneer de gebruiker wordt geverifieerd.
  • Een service AuthorizeService die de details op lager niveau van het verificatieproces verwerkt en informatie over de geverifieerde gebruiker beschikbaar maakt voor de rest van de app voor verbruik.
  • Een Angular-module die routes definieert die zijn gekoppeld aan de verificatieonderdelen van de app. Het stelt het onderdeel van het aanmeldingsmenu, de interceptor, de waakhond en de service bloot voor gebruik door de rest van de app.

Algemene beschrijving van de React-app

De ondersteuning voor verificatie en API-autorisatie in de React-sjabloon bevindt zich in de map ClientApp/src/components/api- authorization. Deze bestaat uit de volgende elementen:

  • 4 onderdelen:
    • Login.js: verwerkt de aanmeldingsstroom van de app.
    • Logout.js: Behandelt het uitlogproces van de app.
    • LoginMenu.js: een widget met een van de volgende sets koppelingen:
      • Beheer van gebruikersprofielen en afmeldingskoppelingen wanneer de gebruiker wordt geverifieerd.
      • Registratie- en inlogkoppelingen wanneer de gebruiker niet is aangemeld.
    • AuthorizeRoute.js: een routeonderdeel waarvoor een gebruiker moet worden geverifieerd voordat het onderdeel wordt weergegeven dat is aangegeven in de parameter Component.
  • Een geëxporteerd authService exemplaar van klasse AuthorizeService die de details van het verificatieproces op lager niveau verwerkt en informatie over de geverifieerde gebruiker beschikbaar maakt voor de rest van de app voor verbruik.

Nu u de belangrijkste onderdelen van de oplossing hebt gezien, kunt u dieper ingaan op afzonderlijke scenario's voor de app.

Autorisatie vereisen voor een nieuwe API

Het systeem is standaard geconfigureerd om eenvoudig autorisatie voor nieuwe API's te vereisen. Hiervoor maakt u een nieuwe controller en voegt u het kenmerk [Authorize] toe aan de controllerklasse of aan een actie binnen de controller.

De API-verificatiehandler aanpassen

Configureer de JwtBearerOptions instantie om de configuratie van de JWT-handler van de API aan te passen:

services.AddAuthentication()
    .AddIdentityServerJwt();

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

De JWT-handler van de API genereert gebeurtenissen die controle over het verificatieproces mogelijk maken met behulp van JwtBearerEvents. Voor ondersteuning voor API-autorisatie registreert AddIdentityServerJwt zijn eigen gebeurtenis-handlers.

Als u de verwerking van een gebeurtenis wilt aanpassen, verpakt u de bestaande gebeurtenis-handler met aanvullende logica zoals vereist. Bijvoorbeeld:

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

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

In de voorgaande code wordt de OnTokenValidated gebeurtenis-handler vervangen door een aangepaste implementatie. Deze implementatie:

  1. Roept de oorspronkelijke implementatie aan die wordt geleverd door de API-autorisatieondersteuning.
  2. Voer een eigen aangepaste logica uit.

Een route aan de clientzijde beveiligen (Angular)

Het beveiligen van een route aan de clientzijde wordt uitgevoerd door de autorisatiebeveiliging toe te voegen aan de lijst met beveiligingsmaatregelen die moeten worden uitgevoerd bij het configureren van een route. U kunt bijvoorbeeld zien hoe de fetch-data route is geconfigureerd in de Angular-module van de hoofd-app:

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

Het is belangrijk te vermelden dat het beveiligen van een route niet het werkelijke eindpunt beveiligt (waarvoor nog steeds een [Authorize] kenmerk is toegepast), maar dat de gebruiker alleen niet naar de opgegeven route aan de clientzijde navigeert wanneer het niet is geverifieerd.

API-aanvragen verifiëren (Angular)

Het verifiëren van aanvragen voor API's die naast de app worden gehost, wordt automatisch uitgevoerd via het gebruik van de HTTP-client-interceptor die is gedefinieerd door de app.

Een route aan de clientzijde beveiligen (React)

Beveilig een client-side route door het AuthorizeRoute-onderdeel te gebruiken in plaats van het gewone Route-onderdeel. U ziet bijvoorbeeld hoe de fetch-data route is geconfigureerd in het App-onderdeel:

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

Een route beveiligen:

  • Beveiligt het werkelijke eindpunt niet (waarvoor nog steeds een [Authorize] kenmerk is vereist dat erop wordt toegepast).
  • Hiermee voorkomt u alleen dat de gebruiker naar de opgegeven route aan de clientzijde navigeert wanneer deze niet is geverifieerd.

API-aanvragen verifiëren (React)

Het verifiëren van aanvragen met React wordt uitgevoerd door eerst het authService exemplaar uit de AuthorizeServicete importeren. Het toegangstoken wordt opgehaald uit de authService en is gekoppeld aan de aanvraag, zoals hieronder wordt weergegeven. In React-onderdelen wordt dit werk doorgaans uitgevoerd in de componentDidMount levenscyclusmethode of als gevolg van een bepaalde gebruikersinteractie.

De authService importeren in een onderdeel

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

Het toegangstoken ophalen en koppelen aan het antwoord

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 });
}

Implementeren in productie

Als u de app wilt implementeren in productie, moeten de volgende resources worden ingericht:

  • Een database voor het opslaan van de Identity gebruikersaccounts en de IdentityServer-toekenningen.
  • Een productiecertificaat om tokens te ondertekenen.
    • Er zijn geen specifieke vereisten voor dit certificaat; het kan een zelfondertekend certificaat zijn of een certificaat dat is ingericht via een CA-instantie.
    • Het kan worden gegenereerd via standaardhulpprogramma's zoals PowerShell of OpenSSL.
    • Het kan worden geïnstalleerd in het certificaatarchief op de doelcomputers of worden geïmplementeerd als een PFX--bestand met een sterk wachtwoord.

Voorbeeld: Implementeren naar een niet-Azure-webhostingprovider

Maak of laad uw certificaat in het webhostingvenster. Wijzig vervolgens in het appsettings.json-bestand van de app de sectie IdentityServer om de belangrijkste details op te nemen. Bijvoorbeeld:

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

In het voorgaande voorbeeld:

  • StoreName vertegenwoordigt de naam van het certificaatarchief waarin het certificaat is opgeslagen. In dit geval verwijst het naar de webhostingwinkel.
  • StoreLocation geeft aan waar het certificaat moet worden geladen (CurrentUser in dit geval).
  • Name komt overeen met het onderscheiden onderwerp voor het certificaat.

Voorbeeld: Implementeren in Azure App Service

In deze sectie wordt beschreven hoe u de app implementeert in Azure App Service met behulp van een certificaat dat is opgeslagen in het certificaatarchief. Als u de app wilt wijzigen om een certificaat uit het certificaatarchief te laden, is een serviceplan voor de Standard-laag of beter vereist wanneer u de app in Azure Portal in een latere stap configureert.

Wijzig in het appsettings.json-bestand van de app de sectie IdentityServer om de belangrijkste details op te nemen:

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "My",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}
  • De winkelnaam vertegenwoordigt de naam van het certificaatarchief waarin het certificaat is opgeslagen. In dit geval verwijst het naar de persoonlijke gebruikerswinkel.
  • De opslaglocatie geeft aan waar het certificaat moet worden geladen (CurrentUser of LocalMachine).
  • De naam van het certificaat komt overeen met het onderscheiden onderwerp voor het certificaat.

Als u naar Azure App Service wilt implementeren, volgt u de stappen in De app implementeren in Azure, waarin wordt uitgelegd hoe u de benodigde Azure-resources maakt en de app implementeert in productie.

Nadat u de voorgaande instructies hebt gevolgd, wordt de app geïmplementeerd in Azure, maar is deze nog niet functioneel. Het certificaat dat door de app wordt gebruikt, moet worden geconfigureerd in Azure Portal. Zoek de vingerafdruk van het certificaat en volg de stappen zoals beschreven in Het laden van uw certificaten.

Hoewel in deze stappen SSL wordt vermeld, is er een sectie privécertificaten in Azure Portal waar u het ingerichte certificaat kunt uploaden voor gebruik met de app.

Nadat u de app en de instellingen van de app in Azure Portal hebt geconfigureerd, start u de app opnieuw op in de portal.

Andere configuratieopties

De ondersteuning voor API-autorisatie bouwt voort op IdentityServer met een set conventies, standaardwaarden en verbeteringen om de ervaring voor SPA's te vereenvoudigen. Onnodig te zeggen, de volledige kracht van IdentityServer is achter de schermen beschikbaar als de ASP.NET Core-integraties uw scenario niet dekken. De ASP.NET Core-ondersteuning is gericht op 'first party'-apps, waar alle apps door onze organisatie worden gemaakt en geïmplementeerd. Daarom wordt ondersteuning niet aangeboden voor zaken zoals toestemming of federatie. Voor deze scenario's gebruikt u IdentityServer en volgt u de bijbehorende documentatie.

Toepassingsprofielen

Toepassingsprofielen zijn vooraf gedefinieerde configuraties voor apps die hun parameters verder definiëren. Op dit moment worden de volgende profielen ondersteund:

  • IdentityServerSPA: Vertegenwoordigt een enkelvoudige pagina-applicatie (SPA) die naast IdentityServer wordt gehost als één geheel.
    • De redirect_uri wordt standaard ingesteld op /authentication/login-callback.
    • De post_logout_redirect_uri wordt standaard ingesteld op /authentication/logout-callback.
    • De set van bereiken omvat openid, profileen elk bereik dat is gedefinieerd voor API's in de app.
    • De set toegestane OIDC-antwoordtypen is id_token token of elk afzonderlijk (id_token, token).
    • De toegestane antwoordmodus is fragment.
  • SPA: Vertegenwoordigt een SPA die niet door IdentityServer wordt gehost.
    • De set van bereiken omvat openid, profileen elk bereik dat voor de API's in de app is gedefinieerd.
    • De set toegestane OIDC-antwoordtypen is id_token token of elk afzonderlijk (id_token, token).
    • De toegestane antwoordmodus is fragment.
  • IdentityServerJwt: Vertegenwoordigt een API die naast IdentityServer wordt gehost.
    • De app is geconfigureerd voor één bereik dat standaard de naam van de app heeft.
  • API: Vertegenwoordigt een API die niet wordt gehost met IdentityServer.
    • De app is geconfigureerd voor één bereik dat standaard de naam van de app heeft.

Configuratie via AppSettings

Configureer de apps via het configuratiesysteem door ze toe te voegen aan de lijst met Clients of Resources.

Configureer de eigenschap redirect_uri en post_logout_redirect_uri van elke client, zoals wordt weergegeven in het volgende voorbeeld:

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

Wanneer u een resource configureert, kunt u de bereiken ervan instellen zoals hieronder wordt weergegeven.

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

Configuratie via code

U kunt de clients en resources ook via code configureren met behulp van een overload-methode van AddApiAuthorization die een actie aanneemt om opties te configureren.

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"));
});

Aanvullende informatiebronnen