Condividi tramite


Eseguire la migrazione dell'autenticazione e Identity a ASP.NET Core 2.0

Di Scott Addie e Hao Kung

ASP.NET Core 2.0 ha un nuovo modello per l'autenticazione e Identity semplifica la configurazione usando i servizi. ASP.NET applicazioni Core 1.x che usano l'autenticazione o Identity possono essere aggiornate per usare il nuovo modello come descritto di seguito.

Aggiornare gli spazi dei nomi

Nella versione 1.x, le classi di questo IdentityRole tipo e IdentityUser sono state trovate nello spazio dei Microsoft.AspNetCore.Identity.EntityFrameworkCore nomi .

Nella versione 2.0, lo Microsoft.AspNetCore.Identity spazio dei nomi è diventato la nuova casa per diverse di queste classi. Con il codice predefinito Identity , le classi interessate includono ApplicationUser e Startup. using Modificare le istruzioni per risolvere i riferimenti interessati.

Middleware e servizi di autenticazione

Nei progetti 1.x l'autenticazione viene configurata tramite middleware. Viene richiamato un metodo middleware per ogni schema di autenticazione che si vuole supportare.

L'esempio 1.x seguente configura l'autenticazione di Facebook con Identity in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>();
}

public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
{
    app.UseIdentity();
    app.UseFacebookAuthentication(new FacebookOptions {
        AppId = Configuration["auth:facebook:appid"],
        AppSecret = Configuration["auth:facebook:appsecret"]
    });
}

Nei progetti 2.0 l'autenticazione viene configurata tramite i servizi. Ogni schema di autenticazione viene registrato nel ConfigureServices metodo di Startup.cs. Il UseIdentity metodo viene sostituito con UseAuthentication.

L'esempio 2.0 seguente configura l'autenticazione di Facebook con Identity in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

    // If you want to tweak Identity cookies, they're no longer part of IdentityOptions.
    services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
    services.AddAuthentication()
            .AddFacebook(options =>
            {
                options.AppId = Configuration["auth:facebook:appid"];
                options.AppSecret = Configuration["auth:facebook:appsecret"];
            });
}

public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) {
    app.UseAuthentication();
}

Il UseAuthentication metodo aggiunge un singolo componente middleware di autenticazione, responsabile dell'autenticazione automatica e della gestione delle richieste di autenticazione remota. Sostituisce tutti i singoli componenti middleware con un singolo componente middleware comune.

Di seguito sono riportate le istruzioni di migrazione 2.0 per ogni schema di autenticazione principale.

Selezionare una delle due opzioni seguenti e apportare le modifiche necessarie in Startup.cs:

  1. Usare cookies con Identity

    • Sostituire UseIdentity con UseAuthentication nel Configure metodo :

      app.UseAuthentication();
      
    • Richiamare il AddIdentity metodo nel ConfigureServices metodo per aggiungere i cookie servizi di autenticazione.

    • Facoltativamente, richiamare il ConfigureApplicationCookie metodo o ConfigureExternalCookie nel ConfigureServices metodo per modificare le Identitycookie impostazioni.

      services.AddIdentity<ApplicationUser, IdentityRole>()
              .AddEntityFrameworkStores<ApplicationDbContext>()
              .AddDefaultTokenProviders();
      
      services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
      
  2. Usare cookies senza Identity

    • Sostituire la chiamata al UseCookieAuthenticationConfigure metodo nel metodo con UseAuthentication:

      app.UseAuthentication();
      
    • Richiamare i AddAuthentication metodi e AddCookie nel ConfigureServices metodo :

      // If you don't want the cookie to be automatically authenticated and assigned to HttpContext.User,
      // remove the CookieAuthenticationDefaults.AuthenticationScheme parameter passed to AddAuthentication.
      services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
              .AddCookie(options =>
              {
                  options.LoginPath = "/Account/LogIn";
                  options.LogoutPath = "/Account/LogOff";
              });
      

Autenticazione del bearer JWT

Modificare Startup.cs nel modo seguente:

  • Sostituire la chiamata al UseJwtBearerAuthenticationConfigure metodo nel metodo con UseAuthentication:

    app.UseAuthentication();
    
  • Richiamare il AddJwtBearer metodo nel ConfigureServices metodo :

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Audience = "http://localhost:5001/";
                options.Authority = "http://localhost:5000/";
            });
    

    Questo frammento di codice non usa Identity, quindi lo schema predefinito deve essere impostato passando JwtBearerDefaults.AuthenticationScheme al AddAuthentication metodo .

Autenticazione OIDC (OpenID Connessione)

Modificare Startup.cs nel modo seguente:

  • Sostituire la chiamata al UseOpenIdConnectAuthenticationConfigure metodo nel metodo con UseAuthentication:

    app.UseAuthentication();
    
  • Richiamare il AddOpenIdConnect metodo nel ConfigureServices metodo :

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(options =>
    {
        options.Authority = Configuration["auth:oidc:authority"];
        options.ClientId = Configuration["auth:oidc:clientid"];
    });
    
  • Sostituire la PostLogoutRedirectUri proprietà nell'azione OpenIdConnectOptions con SignedOutRedirectUri:

    .AddOpenIdConnect(options =>
    {
        options.SignedOutRedirectUri = "https://contoso.com";
    });
    

Autenticazione di Facebook

Modificare Startup.cs nel modo seguente:

  • Sostituire la chiamata al UseFacebookAuthenticationConfigure metodo nel metodo con UseAuthentication:

    app.UseAuthentication();
    
  • Richiamare il AddFacebook metodo nel ConfigureServices metodo :

    services.AddAuthentication()
            .AddFacebook(options =>
            {
                options.AppId = Configuration["auth:facebook:appid"];
                options.AppSecret = Configuration["auth:facebook:appsecret"];
            });
    

Autenticazione di Google

Modificare Startup.cs nel modo seguente:

  • Sostituire la chiamata al UseGoogleAuthenticationConfigure metodo nel metodo con UseAuthentication:

    app.UseAuthentication();
    
  • Richiamare il AddGoogle metodo nel ConfigureServices metodo :

    services.AddAuthentication()
            .AddGoogle(options =>
            {
                options.ClientId = Configuration["auth:google:clientid"];
                options.ClientSecret = Configuration["auth:google:clientsecret"];
            });
    

Autenticazione di account Microsoft

Per altre informazioni sull'autenticazione dell'account Microsoft, vedere questo problema di GitHub.

Modificare Startup.cs nel modo seguente:

  • Sostituire la chiamata al UseMicrosoftAccountAuthenticationConfigure metodo nel metodo con UseAuthentication:

    app.UseAuthentication();
    
  • Richiamare il AddMicrosoftAccount metodo nel ConfigureServices metodo :

    services.AddAuthentication()
            .AddMicrosoftAccount(options =>
            {
                options.ClientId = Configuration["auth:microsoft:clientid"];
                options.ClientSecret = Configuration["auth:microsoft:clientsecret"];
            });
    

Autenticazione di Twitter

Modificare Startup.cs nel modo seguente:

  • Sostituire la chiamata al UseTwitterAuthenticationConfigure metodo nel metodo con UseAuthentication:

    app.UseAuthentication();
    
  • Richiamare il AddTwitter metodo nel ConfigureServices metodo :

    services.AddAuthentication()
            .AddTwitter(options =>
            {
                options.ConsumerKey = Configuration["auth:twitter:consumerkey"];
                options.ConsumerSecret = Configuration["auth:twitter:consumersecret"];
            });
    

Impostazione di schemi di autenticazione predefiniti

Nella versione 1.x le AutomaticAuthenticate proprietà e AutomaticChallenge della AuthenticationOptions classe base devono essere impostate su un singolo schema di autenticazione. Non c'era un buon modo per imporre questo.

Nella versione 2.0 queste due proprietà sono state rimosse come proprietà nella singola AuthenticationOptions istanza. Possono essere configurati nella chiamata al AddAuthentication metodo all'interno del ConfigureServices metodo di Startup.cs:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);

Nel frammento di codice precedente lo schema predefinito è impostato su CookieAuthenticationDefaults.AuthenticationScheme ("Cookies").

In alternativa, usare una versione di overload del AddAuthentication metodo per impostare più proprietà. Nell'esempio di metodo di overload seguente, lo schema predefinito è impostato su CookieAuthenticationDefaults.AuthenticationScheme. In alternativa, lo schema di autenticazione può essere specificato all'interno dei singoli [Authorize] attributi o criteri di autorizzazione.

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
});

Definire uno schema predefinito nella versione 2.0 se una delle condizioni seguenti è vera:

  • Si vuole che l'utente sia connesso automaticamente
  • Usare i [Authorize] criteri di autorizzazione o attributo senza specificare schemi

Un'eccezione a questa regola è il AddIdentity metodo . Questo metodo aggiunge cookieautomaticamente gli schemi di autenticazione e verifica predefiniti all'applicazione cookieIdentityConstants.ApplicationScheme. Inoltre, imposta lo schema di accesso predefinito sull'oggetto esterno cookieIdentityConstants.ExternalScheme.

Usare le estensioni di autenticazione HttpContext

L'interfaccia IAuthenticationManager è il punto di ingresso principale nel sistema di autenticazione 1.x. È stato sostituito con un nuovo set di metodi di HttpContext estensione nello spazio dei Microsoft.AspNetCore.Authentication nomi .

Ad esempio, i progetti 1.x fanno riferimento a una Authentication proprietà:

// Clear the existing external cookie to ensure a clean login process
await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);

Nei progetti 2.0 importare lo Microsoft.AspNetCore.Authentication spazio dei nomi ed eliminare i riferimenti alle Authentication proprietà:

// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

Autenticazione di Windows (HTTP.sys/IISIntegration)

Esistono due varianti di autenticazione di Windows:

  • L'host consente solo gli utenti autenticati. Questa variante non è influenzata dalle modifiche 2.0.

  • L'host consente sia utenti anonimi che autenticati. Questa variazione è influenzata dalle modifiche della versione 2.0. Ad esempio, l'app deve consentire utenti anonimi a livello IIS o HTTP.sys , ma autorizzare gli utenti a livello di controller. In questo scenario, impostare lo schema predefinito nel Startup.ConfigureServices metodo .

    Per Microsoft.AspNetCore.Server.IISIntegration, impostare lo schema predefinito su IISDefaults.AuthenticationScheme:

    using Microsoft.AspNetCore.Server.IISIntegration;
    
    services.AddAuthentication(IISDefaults.AuthenticationScheme);
    

    Per Microsoft.AspNetCore.Server.HttpSys, impostare lo schema predefinito su HttpSysDefaults.AuthenticationScheme:

    using Microsoft.AspNetCore.Server.HttpSys;
    
    services.AddAuthentication(HttpSysDefaults.AuthenticationScheme);
    

    Se non si imposta lo schema predefinito, la richiesta di autorizzazione (richiesta di verifica) non funziona con l'eccezione seguente:

    System.InvalidOperationException: non è stato specificato alcun authenticationScheme e non è stato trovato DefaultChallengeScheme.

Per altre informazioni, vedere Configurare l'autenticazione di Windows in ASP.NET Core.

IdentityCookieIstanze delle opzioni

Un effetto collaterale delle modifiche 2.0 è il passaggio all'uso delle opzioni denominate anziché delle istanze di cookie opzioni. La possibilità di personalizzare i nomi degli Identitycookie schemi viene rimossa.

Ad esempio, i progetti 1.x usano l'inserimento del costruttore per passare un IdentityCookieOptions parametro in AccountController.cs e ManageController.cs. È possibile accedere allo schema di autenticazione esterno cookie dall'istanza fornita:

public AccountController(
    UserManager<ApplicationUser> userManager,
    SignInManager<ApplicationUser> signInManager,
    IOptions<IdentityCookieOptions> identityCookieOptions,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _externalCookieScheme = identityCookieOptions.Value.ExternalCookieAuthenticationScheme;
    _emailSender = emailSender;
    _smsSender = smsSender;
    _logger = loggerFactory.CreateLogger<AccountController>();
}

L'inserimento del costruttore precedente non diventa necessario nei progetti 2.0 e il _externalCookieScheme campo può essere eliminato:

public AccountController(
    UserManager<ApplicationUser> userManager,
    SignInManager<ApplicationUser> signInManager,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _emailSender = emailSender;
    _smsSender = smsSender;
    _logger = loggerFactory.CreateLogger<AccountController>();
}

1.x progetti usati nel _externalCookieScheme campo come indicato di seguito:

// Clear the existing external cookie to ensure a clean login process
await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);

Nei progetti 2.0 sostituire il codice precedente con il codice seguente. La IdentityConstants.ExternalScheme costante può essere utilizzata direttamente.

// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

Risolvere la chiamata appena aggiunta SignOutAsync importando lo spazio dei nomi seguente:

using Microsoft.AspNetCore.Authentication;

Aggiungere Identityproprietà di navigazione POCO utente

Le proprietà di navigazione Entity Framework (EF) Core dell'oggetto IdentityUser CLR semplice (Plain Old CLR Object) sono state rimosse. Se il progetto 1.x ha usato queste proprietà, aggiungerle manualmente al progetto 2.0:

/// <summary>
/// Navigation property for the roles this user belongs to.
/// </summary>
public virtual ICollection<IdentityUserRole<int>> Roles { get; } = new List<IdentityUserRole<int>>();

/// <summary>
/// Navigation property for the claims this user possesses.
/// </summary>
public virtual ICollection<IdentityUserClaim<int>> Claims { get; } = new List<IdentityUserClaim<int>>();

/// <summary>
/// Navigation property for this users login accounts.
/// </summary>
public virtual ICollection<IdentityUserLogin<int>> Logins { get; } = new List<IdentityUserLogin<int>>();

Per evitare chiavi esterne duplicate durante l'esecuzione EF Core di Migrazioni, aggiungere quanto segue al IdentityDbContext metodo della OnModelCreating classe (dopo la base.OnModelCreating(); chiamata):

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    // Customize the ASP.NET Core Identity model and override the defaults if needed.
    // For example, you can rename the ASP.NET Core Identity table names and more.
    // Add your customizations after calling base.OnModelCreating(builder);

    builder.Entity<ApplicationUser>()
        .HasMany(e => e.Claims)
        .WithOne()
        .HasForeignKey(e => e.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Cascade);

    builder.Entity<ApplicationUser>()
        .HasMany(e => e.Logins)
        .WithOne()
        .HasForeignKey(e => e.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Cascade);

    builder.Entity<ApplicationUser>()
        .HasMany(e => e.Roles)
        .WithOne()
        .HasForeignKey(e => e.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Cascade);
}

Sostituire GetExternalAuthenticationSchemes

Il metodo GetExternalAuthenticationSchemes sincrono è stato rimosso a favore di una versione asincrona. I progetti 1.x hanno il codice seguente in Controllers/ManageController.cs:

var otherLogins = _signInManager.GetExternalAuthenticationSchemes().Where(auth => userLogins.All(ul => auth.AuthenticationScheme != ul.LoginProvider)).ToList();

Questo metodo viene visualizzato anche in Views/Account/Login.cshtml :

@{
    var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList();
    if (loginProviders.Count == 0)
    {
        <div>
            <p>
                There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                for details on setting up this ASP.NET application to support logging in via external services.
            </p>
        </div>
    }
    else
    {
        <form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
            <div>
                <p>
                    @foreach (var provider in loginProviders)
                    {
                        <button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
                    }
                </p>
            </div>
        </form>
    }
}

Nei progetti 2.0 usare il GetExternalAuthenticationSchemesAsync metodo . La modifica in ManageController.cs è simile al codice seguente:

var schemes = await _signInManager.GetExternalAuthenticationSchemesAsync();
var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList();

In Login.cshtmlla AuthenticationScheme proprietà a cui si accede nel foreach ciclo passa a Name:

@{
    var loginProviders = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList();
    if (loginProviders.Count == 0)
    {
        <div>
            <p>
                There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                for details on setting up this ASP.NET application to support logging in via external services.
            </p>
        </div>
    }
    else
    {
        <form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
            <div>
                <p>
                    @foreach (var provider in loginProviders)
                    {
                        <button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                    }
                </p>
            </div>
        </form>
    }
}

Modifica della proprietà ManageLoginsViewModel

Un ManageLoginsViewModel oggetto viene utilizzato nell'azione ManageLogins di ManageController.cs. Nei progetti 1.x il tipo restituito della proprietà dell'oggetto OtherLogins è IList<AuthenticationDescription>. Questo tipo restituito richiede un'importazione di Microsoft.AspNetCore.Http.Authentication:

using System.Collections.Generic;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Identity;

namespace AspNetCoreDotNetCore1App.Models.ManageViewModels
{
    public class ManageLoginsViewModel
    {
        public IList<UserLoginInfo> CurrentLogins { get; set; }

        public IList<AuthenticationDescription> OtherLogins { get; set; }
    }
}

Nei progetti 2.0 il tipo restituito cambia in IList<AuthenticationScheme>. Questo nuovo tipo restituito richiede la sostituzione dell'importazione Microsoft.AspNetCore.Http.Authentication con un'importazione Microsoft.AspNetCore.Authentication .

using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;

namespace AspNetCoreDotNetCore2App.Models.ManageViewModels
{
    public class ManageLoginsViewModel
    {
        public IList<UserLoginInfo> CurrentLogins { get; set; }

        public IList<AuthenticationScheme> OtherLogins { get; set; }
    }
}

Risorse aggiuntive

Per altre informazioni, vedere la discussione relativa al problema relativo all'autenticazione 2.0 in GitHub.