Introduzione a Identity in ASP.NET Core

Di Rick Anderson

ASP.NET Core Identity:

  • API che supporta la funzionalità di accesso dell'interfaccia utente.
  • Gestisce utenti, password, dati del profilo, ruoli, attestazioni, token, conferma tramite posta elettronica e altro ancora.

Gli utenti possono creare un account con le informazioni di accesso archiviate in Identity oppure possono usare un provider di accesso esterno. I provider di accesso esterni supportati includono Facebook, Google, Account Microsoft e Twitter.

Per informazioni su come richiedere a livello globale l'autenticazione di tutti gli utenti, vedere Richiedere utenti autenticati.

Il Identity codice sorgente è disponibile in GitHub. Eseguire lo scaffolding Identity e visualizzare i file generati per esaminare l'interazione del modello con Identity.

Identity viene in genere configurato usando un database di SQL Server per archiviare nomi utente, password e dati del profilo. In alternativa, è possibile usare un altro archivio permanente, ad esempio tabella di Azure Archiviazione.

In questo argomento si apprenderà come usare Identity per registrare, accedere e disconnettere un utente. Nota: i modelli trattano nome utente e posta elettronica come gli stessi per gli utenti. Per istruzioni più dettagliate sulla creazione di app che usano Identity, vedere Passaggi successivi.

ASP.NET Core Identity non è correlato a Microsoft Identity Platform. Microsoft Identity Platform è:

  • Evoluzione della piattaforma per sviluppatori di Azure Active Directory (Azure AD).
  • Soluzione alternativa di identità per l'autenticazione e l'autorizzazione nelle app ASP.NET Core.

ASP.NET Core Identity aggiunge funzionalità di accesso dell'interfaccia utente alle app Web core ASP.NET. Per proteggere le API Web e i contratti a pagina singola, usare una delle opzioni seguenti:

Duende Identity Server è un framework OpenID Connessione e OAuth 2.0 per ASP.NET Core. Duende Identity Server abilita le funzionalità di sicurezza seguenti:

  • Autenticazione come servizio (AaaS)
  • Single Sign-On/off (SSO) su più tipi di applicazione
  • Controllo di accesso per le API
  • Gateway federativo

Importante

Duende Software potrebbe richiedere il pagamento di una tariffa di licenza per l'uso in produzione di Duende Identity Server. Per altre informazioni, vedere Eseguire la migrazione da ASP.NET Core 5.0 a 6.0.

Per altre informazioni, vedere la documentazione di Duende Server (sito Web duende Identity Software).

Visualizzare o scaricare il codice di esempio (come scaricare).

Creare un'app Web con l'autenticazione

Creare un progetto di applicazione Web di base ASP.NET con singoli account utente.

  • Selezionare il modello App Web ASP.NET Core. Denominare il progetto WebApp1 per avere lo stesso spazio dei nomi del download del progetto. Fare clic su OK.
  • Per Tipo di autenticazione, selezionare Account utente individuali.

Il progetto generato fornisce ASP.NET CoreIdentitycome libreria di Razor classi. La IdentityRazor libreria di classi espone gli endpoint con l'area Identity . Ad esempio:

  • /Identity/Account/Login
  • /Identity/Account/Disconnessione
  • /Identity/Account/Manage

Applicare le migrazioni

Applicare le migrazioni per inizializzare il database.

Eseguire il comando seguente nella console Gestione pacchetti (PMC):

Update-Database

Test Register and Login

Eseguire l'app e registrare un utente. A seconda delle dimensioni dello schermo, potrebbe essere necessario selezionare l'interruttore di spostamento per visualizzare i collegamenti Registra e Account di accesso .

Visualizzare il Identity database

  • Scegliere SQL Server Esplora oggetti (SSOX) dal menu Visualizza.
  • Passare a (localdb)MSSQL Local DB(SQL Server 13). Fare clic con il pulsante destro del mouse su dbo. AspNetUsers>Visualizza dati:

Contextual menu on AspNetUsers table in SQL Server Object Explorer

Configurare i Identity servizi

I servizi vengono aggiunti in Program.cs. Il modello tipico consiste nel chiamare i metodi nell'ordine seguente:

  1. Add{Service}
  2. builder.Services.Configure{Service}
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

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

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

builder.Services.Configure<IdentityOptions>(options =>
{
    // Password settings.
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;

    // Lockout settings.
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;

    // User settings.
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
    options.User.RequireUniqueEmail = false;
});

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    options.LoginPath = "/Identity/Account/Login";
    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
    options.SlidingExpiration = true;
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

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

app.UseRouting();

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

app.MapRazorPages();

app.Run();

Il codice precedente viene configurato Identity con valori di opzione predefiniti. I servizi vengono resi disponibili all'app tramite l'inserimento delle dipendenze.

Identity è abilitato chiamando UseAuthentication. UseAuthenticationaggiunge il middleware di autenticazione alla pipeline di richiesta.

L'app generata dal modello non usa l'autorizzazione. app.UseAuthorization è incluso per assicurarsi che venga aggiunto nell'ordine corretto se l'app aggiunge l'autorizzazione. UseRouting, UseAuthenticatione UseAuthorization devono essere chiamati nell'ordine indicato nel codice precedente.

Per altre informazioni su IdentityOptions, vedere IdentityOptions e Avvio dell'applicazione.

Eseguire lo scaffolding di Register, Login, LogOut e RegisterConfirmation

Aggiungere i Registerfile , LogOutLogin, e RegisterConfirmation . Seguire l'identità dello scaffolding in un Razor progetto con istruzioni di autorizzazione per generare il codice illustrato in questa sezione.

Esaminare il registro

Quando un utente fa clic sul pulsante Registra nella Register pagina, viene richiamata l'azione RegisterModel.OnPostAsync . L'utente viene creato dall'oggetto CreateAsync(TUser)_userManager :

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Disabilitare la verifica dell'account predefinita

Con i modelli predefiniti, l'utente viene reindirizzato alla Account.RegisterConfirmation posizione in cui può selezionare un collegamento per confermare l'account. Il valore predefinito viene usato solo per i test, la verifica automatica dell'account deve essere disabilitata Account.RegisterConfirmation in un'app di produzione.

Per richiedere un account confermato e impedire l'accesso immediato durante la registrazione, impostare DisplayConfirmAccountLink = false in /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs:

[AllowAnonymous]
public class RegisterConfirmationModel : PageModel
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IEmailSender _sender;

    public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
    {
        _userManager = userManager;
        _sender = sender;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return NotFound($"Unable to load user with email '{email}'.");
        }

        Email = email;
        // Once you add a real email sender, you should remove this code that lets you confirm the account
        DisplayConfirmAccountLink = false;
        if (DisplayConfirmAccountLink)
        {
            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            EmailConfirmationUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);
        }

        return Page();
    }
}

Accesso

Il modulo Login viene visualizzato quando:

  • Il collegamento Accedi è selezionato.
  • Un utente tenta di accedere a una pagina con restrizioni che non è autorizzata ad accedere o quando non è stata autenticata dal sistema.

Quando viene inviato il modulo nella pagina Account di accesso, viene chiamata l'azione OnPostAsync . PasswordSignInAsync viene chiamato sull'oggetto _signInManager .

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Per informazioni su come prendere decisioni di autorizzazione, vedere Introduzione all'autorizzazione in ASP.NET Core.

Effettuare la disconnessione

Il collegamento Disconnettersi richiama l'azione LogoutModel.OnPost .

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

Nel codice precedente, il codice return RedirectToPage(); deve essere un reindirizzamento in modo che il browser esegua una nuova richiesta e l'identità per l'utente venga aggiornata.

SignOutAsync cancella le attestazioni dell'utente archiviate in un oggetto cookie.

Post è specificato in Pages/Shared/_LoginPartial.cshtml:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

Test Identity

I modelli di progetto Web predefiniti consentono l'accesso anonimo alle home page. Per testare Identity, aggiungere [Authorize]:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Se è stato eseguito l'accesso, disconnettersi. Eseguire l'app e selezionare il Privacy collegamento. Si verrà reindirizzati alla pagina di accesso.

Esplora Identity

Per esplorare Identity in modo più dettagliato:

Identity Components

Tutti i Identitypacchetti NuGet dipendenti da sono inclusi nel framework condiviso di ASP.NET Core.

Il pacchetto primario per Identity è Microsoft.AspNetCore.Identity. Questo pacchetto contiene il set di base di interfacce per ASP.NET Core Identityed è incluso da Microsoft.AspNetCore.Identity.EntityFrameworkCore.

Migrazione a ASP.NET Core Identity

Per altre informazioni e indicazioni sulla migrazione dell'archivio esistente Identity , vedere Eseguire la migrazione dell'autenticazione e Identity.

Impostazione della complessità della password

Vedere Configurazione per un esempio che imposta i requisiti minimi per le password.

AddDefaultIdentity e AggiungiIdentity

AddDefaultIdentity è stato introdotto in ASP.NET Core 2.1. La chiamata AddDefaultIdentity è simile alla chiamata seguente:

Per altre informazioni, vedere AddDefault source .See AddDefaultIdentity source for more information.

Impedire la pubblicazione di asset statici Identity

Per impedire la pubblicazione di asset statici Identity (fogli di stile e file JavaScript per Identity l'interfaccia utente) nella radice Web, aggiungere la proprietà RemoveIdentityAssets e la destinazione seguenti ResolveStaticWebAssetsInputsDependsOn al file di progetto dell'app:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

Passaggi successivi

Di Rick Anderson

ASP.NET Core Identity:

  • API che supporta la funzionalità di accesso dell'interfaccia utente.
  • Gestisce utenti, password, dati del profilo, ruoli, attestazioni, token, conferma tramite posta elettronica e altro ancora.

Gli utenti possono creare un account con le informazioni di accesso archiviate in Identity oppure possono usare un provider di accesso esterno. I provider di accesso esterni supportati includono Facebook, Google, Account Microsoft e Twitter.

Per informazioni su come richiedere a livello globale l'autenticazione di tutti gli utenti, vedere Richiedere utenti autenticati.

Il Identity codice sorgente è disponibile in GitHub. Eseguire lo scaffolding Identity e visualizzare i file generati per esaminare l'interazione del modello con Identity.

Identity viene in genere configurato usando un database di SQL Server per archiviare nomi utente, password e dati del profilo. In alternativa, è possibile usare un altro archivio permanente, ad esempio tabella di Azure Archiviazione.

In questo argomento si apprenderà come usare Identity per registrare, accedere e disconnettere un utente. Nota: i modelli trattano nome utente e posta elettronica come gli stessi per gli utenti. Per istruzioni più dettagliate sulla creazione di app che usano Identity, vedere Passaggi successivi.

Microsoft Identity Platform è:

  • Evoluzione della piattaforma per sviluppatori di Azure Active Directory (Azure AD).
  • Soluzione alternativa di identità per l'autenticazione e l'autorizzazione nelle app ASP.NET Core.
  • Non correlato a ASP.NET Core Identity.

ASP.NET Core Identity aggiunge funzionalità di accesso dell'interfaccia utente alle app Web core ASP.NET. Per proteggere le API Web e i contratti a pagina singola, usare una delle opzioni seguenti:

Duende IdentityServer è un framework OpenID Connessione e OAuth 2.0 per ASP.NET Core. Duende IdentityServer abilita le funzionalità di sicurezza seguenti:

  • Autenticazione come servizio (AaaS)
  • Single Sign-On/off (SSO) su più tipi di applicazione
  • Controllo di accesso per le API
  • Gateway federativo

Per altre informazioni, vedere Panoramica di Duende IdentityServer.

Per altre informazioni su altri provider di autenticazione, vedere Opzioni di autenticazione del sistema operativo della community per ASP.NET Core

Visualizzare o scaricare il codice di esempio (come scaricare).

Creare un'app Web con l'autenticazione

Creare un progetto di applicazione Web di base ASP.NET con singoli account utente.

  • Selezionare File>New (Nuovo) >Project (Progetto).
  • Selezionare Applicazione Web ASP.NET Core. Denominare il progetto WebApp1 per avere lo stesso spazio dei nomi del download del progetto. Fare clic su OK.
  • Selezionare un'applicazione Web principale ASP.NET, quindi selezionare Modifica autenticazione.
  • Selezionare Account utente singoli e fare clic su OK.

Il progetto generato fornisce ASP.NET CoreIdentitycome libreria di Razor classi. La IdentityRazor libreria di classi espone gli endpoint con l'area Identity . Ad esempio:

  • /Identity/Account/Login
  • /Identity/Account/Disconnessione
  • /Identity/Account/Manage

Applicare le migrazioni

Applicare le migrazioni per inizializzare il database.

Eseguire il comando seguente nella console Gestione pacchetti (PMC):

PM> Update-Database

Test Register and Login

Eseguire l'app e registrare un utente. A seconda delle dimensioni dello schermo, potrebbe essere necessario selezionare l'interruttore di spostamento per visualizzare i collegamenti Registra e Account di accesso .

Visualizzare il Identity database

  • Scegliere SQL Server Esplora oggetti (SSOX) dal menu Visualizza.
  • Passare a (localdb)MSSQL Local DB(SQL Server 13). Fare clic con il pulsante destro del mouse su dbo. AspNetUsers>Visualizza dati:

Contextual menu on AspNetUsers table in SQL Server Object Explorer

Configurare i Identity servizi

I servizi vengono aggiunti in ConfigureServices. Il modello tipico consiste nel chiamare tutti i metodi Add{Service} e quindi chiamare tutti i metodi services.Configure{Service}.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
     // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

Il codice evidenziato precedente viene configurato Identity con i valori di opzione predefiniti. I servizi vengono resi disponibili all'app tramite l'inserimento delle dipendenze.

Identity è abilitato chiamando UseAuthentication. UseAuthenticationaggiunge il middleware di autenticazione alla pipeline di richiesta.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

Il codice precedente viene configurato Identity con valori di opzione predefiniti. I servizi vengono resi disponibili all'app tramite l'inserimento delle dipendenze.

Identity è abilitato chiamando UseAuthentication. UseAuthenticationaggiunge il middleware di autenticazione alla pipeline di richiesta.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

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

    app.UseRouting();

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

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

L'app generata dal modello non usa l'autorizzazione. app.UseAuthorization è incluso per assicurarsi che venga aggiunto nell'ordine corretto se l'app aggiunge l'autorizzazione. UseRoutingUseAuthorization, UseAuthentication, e UseEndpoints devono essere chiamati nell'ordine indicato nel codice precedente.

Per altre informazioni su IdentityOptions e , vedere IdentityOptions e StartupAvvio dell'applicazione.

Eseguire lo scaffolding di Register, Login, LogOut e RegisterConfirmation

Aggiungere i Registerfile , LogOutLogin, e RegisterConfirmation . Seguire l'identità dello scaffolding in un Razor progetto con istruzioni di autorizzazione per generare il codice illustrato in questa sezione.

Esaminare il registro

Quando un utente fa clic sul pulsante Registra nella Register pagina, viene richiamata l'azione RegisterModel.OnPostAsync . L'utente viene creato dall'oggetto CreateAsync(TUser)_userManager :

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Disabilitare la verifica dell'account predefinita

Con i modelli predefiniti, l'utente viene reindirizzato alla Account.RegisterConfirmation posizione in cui può selezionare un collegamento per confermare l'account. Il valore predefinito viene usato solo per i test, la verifica automatica dell'account deve essere disabilitata Account.RegisterConfirmation in un'app di produzione.

Per richiedere un account confermato e impedire l'accesso immediato durante la registrazione, impostare DisplayConfirmAccountLink = false in /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs:

[AllowAnonymous]
public class RegisterConfirmationModel : PageModel
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IEmailSender _sender;

    public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
    {
        _userManager = userManager;
        _sender = sender;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return NotFound($"Unable to load user with email '{email}'.");
        }

        Email = email;
        // Once you add a real email sender, you should remove this code that lets you confirm the account
        DisplayConfirmAccountLink = false;
        if (DisplayConfirmAccountLink)
        {
            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            EmailConfirmationUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);
        }

        return Page();
    }
}

Accesso

Il modulo Login viene visualizzato quando:

  • Il collegamento Accedi è selezionato.
  • Un utente tenta di accedere a una pagina con restrizioni che non è autorizzata ad accedere o quando non è stata autenticata dal sistema.

Quando viene inviato il modulo nella pagina Account di accesso, viene chiamata l'azione OnPostAsync . PasswordSignInAsync viene chiamato sull'oggetto _signInManager .

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Per informazioni su come prendere decisioni di autorizzazione, vedere Introduzione all'autorizzazione in ASP.NET Core.

Effettuare la disconnessione

Il collegamento Disconnettersi richiama l'azione LogoutModel.OnPost .

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

Nel codice precedente, il codice return RedirectToPage(); deve essere un reindirizzamento in modo che il browser esegua una nuova richiesta e l'identità per l'utente venga aggiornata.

SignOutAsync cancella le attestazioni dell'utente archiviate in un oggetto cookie.

Post è specificato in Pages/Shared/_LoginPartial.cshtml:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

Test Identity

I modelli di progetto Web predefiniti consentono l'accesso anonimo alle home page. Per testare Identity, aggiungere [Authorize]:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Se è stato eseguito l'accesso, disconnettersi. Eseguire l'app e selezionare il Privacy collegamento. Si verrà reindirizzati alla pagina di accesso.

Esplora Identity

Per esplorare Identity in modo più dettagliato:

Identity Components

Tutti i Identitypacchetti NuGet dipendenti da sono inclusi nel framework condiviso di ASP.NET Core.

Il pacchetto primario per Identity è Microsoft.AspNetCore.Identity. Questo pacchetto contiene il set di base di interfacce per ASP.NET Core Identityed è incluso da Microsoft.AspNetCore.Identity.EntityFrameworkCore.

Migrazione a ASP.NET Core Identity

Per altre informazioni e indicazioni sulla migrazione dell'archivio esistente Identity , vedere Eseguire la migrazione dell'autenticazione e Identity.

Impostazione della complessità della password

Vedere Configurazione per un esempio che imposta i requisiti minimi per le password.

Impedire la pubblicazione di asset statici Identity

Per impedire la pubblicazione di asset statici Identity (fogli di stile e file JavaScript per Identity l'interfaccia utente) nella radice Web, aggiungere la proprietà RemoveIdentityAssets e la destinazione seguenti ResolveStaticWebAssetsInputsDependsOn al file di progetto dell'app:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

Passaggi successivi