Migrowanie uwierzytelniania i Identity do ASP.NET Core 2.0

Autor: Scott Addie i Hao Kung

ASP.NET Core 2.0 ma nowy model uwierzytelniania i Identity upraszcza konfigurację przy użyciu usług. ASP.NET aplikacje core 1.x korzystające z uwierzytelniania lub Identity można je zaktualizować, aby korzystać z nowego modelu, jak opisano poniżej.

Aktualizowanie przestrzeni nazw

W wersji 1.x klasy takie IdentityRole i IdentityUser zostały znalezione w Microsoft.AspNetCore.Identity.EntityFrameworkCore przestrzeni nazw.

W wersji 2.0 Microsoft.AspNetCore.Identity przestrzeń nazw stała się nowym domem dla kilku takich klas. W przypadku domyślnego Identity kodu klasy, których dotyczy problem, obejmują ApplicationUser i Startup. Dostosuj instrukcje using , aby rozwiązać problemy z odwołaniami, których dotyczy problem.

Oprogramowanie pośredniczące uwierzytelniania i usługi

W projektach 1.x uwierzytelnianie jest konfigurowane za pośrednictwem oprogramowania pośredniczącego. Metoda oprogramowania pośredniczącego jest wywoływana dla każdego schematu uwierzytelniania, który chcesz obsługiwać.

Poniższy przykład w wersji 1.x umożliwia skonfigurowanie uwierzytelniania serwisu Facebook za pomocą Identity polecenia w programie :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"]
    });
}

W projektach 2.0 uwierzytelnianie jest konfigurowane za pośrednictwem usług. Każdy schemat uwierzytelniania jest zarejestrowany w ConfigureServices metodzie Startup.cs. Metoda UseIdentity jest zastępowana ciągiem UseAuthentication.

Poniższy przykład 2.0 umożliwia skonfigurowanie uwierzytelniania w usłudze Facebook za pomocą Identity polecenia w programie :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();
}

Metoda UseAuthentication dodaje pojedynczy składnik oprogramowania pośredniczącego uwierzytelniania, który jest odpowiedzialny za automatyczne uwierzytelnianie i obsługę żądań uwierzytelniania zdalnego. Zastępuje on wszystkie poszczególne składniki oprogramowania pośredniczącego jednym, typowym składnikiem oprogramowania pośredniczącego.

Poniżej przedstawiono instrukcje migracji 2.0 dla każdego głównego schematu uwierzytelniania.

Wybierz jedną z dwóch poniższych opcji i wprowadź niezbędne zmiany w pliku Startup.cs:

  1. Używanie funkcji cookies z Identity

    • Zastąp UseIdentity ciąg ciągiem UseAuthentication w metodzie Configure :

      app.UseAuthentication();
      
    • Wywołaj metodę AddIdentity w metodzie ConfigureServices , aby dodać cookie usługi uwierzytelniania.

    • Opcjonalnie wywołaj metodę ConfigureApplicationCookie or ConfigureExternalCookie w metodzie ConfigureServices , aby dostosować Identitycookie ustawienia.

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

    • Zastąp UseCookieAuthentication wywołanie metody w metodzie Configure ciągiem UseAuthentication:

      app.UseAuthentication();
      
    • Wywołaj AddAuthentication metody i w metodzie ConfigureServices :AddCookie

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

Uwierzytelnianie elementu nośnego JWT

Wprowadź następujące zmiany w pliku Startup.cs:

  • Zastąp UseJwtBearerAuthentication wywołanie metody w metodzie Configure ciągiem UseAuthentication:

    app.UseAuthentication();
    
  • Wywołaj metodę AddJwtBearer w metodzie ConfigureServices :

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

    Ten fragment kodu nie używa Identityelementu , więc schemat domyślny powinien zostać ustawiony przez przekazanie JwtBearerDefaults.AuthenticationScheme do AddAuthentication metody .

Uwierzytelnianie Połączenie OpenID (OIDC)

Wprowadź następujące zmiany w pliku Startup.cs:

  • Zastąp UseOpenIdConnectAuthentication wywołanie metody w metodzie Configure ciągiem UseAuthentication:

    app.UseAuthentication();
    
  • Wywołaj metodę AddOpenIdConnect w metodzie ConfigureServices :

    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"];
    });
    
  • Zastąp PostLogoutRedirectUri właściwość w OpenIdConnectOptions akcji ciągiem SignedOutRedirectUri:

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

Uwierzytelnianie za pomocą konta Facebook

Wprowadź następujące zmiany w pliku Startup.cs:

  • Zastąp UseFacebookAuthentication wywołanie metody w metodzie Configure ciągiem UseAuthentication:

    app.UseAuthentication();
    
  • Wywołaj metodę AddFacebook w metodzie ConfigureServices :

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

Uwierzytelnianie za pomocą konta Google

Wprowadź następujące zmiany w pliku Startup.cs:

  • Zastąp UseGoogleAuthentication wywołanie metody w metodzie Configure ciągiem UseAuthentication:

    app.UseAuthentication();
    
  • Wywołaj metodę AddGoogle w metodzie ConfigureServices :

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

Uwierzytelnianie za pomocą konta Microsoft

Aby uzyskać więcej informacji na temat uwierzytelniania konta Microsoft, zobacz ten problem z usługą GitHub.

Wprowadź następujące zmiany w pliku Startup.cs:

  • Zastąp UseMicrosoftAccountAuthentication wywołanie metody w metodzie Configure ciągiem UseAuthentication:

    app.UseAuthentication();
    
  • Wywołaj metodę AddMicrosoftAccount w metodzie ConfigureServices :

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

Uwierzytelnianie za pomocą konta Twitter

Wprowadź następujące zmiany w pliku Startup.cs:

  • Zastąp UseTwitterAuthentication wywołanie metody w metodzie Configure ciągiem UseAuthentication:

    app.UseAuthentication();
    
  • Wywołaj metodę AddTwitter w metodzie ConfigureServices :

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

Ustawianie domyślnych schematów uwierzytelniania

W wersji 1.x AutomaticAuthenticate właściwości i AutomaticChallenge klasy bazowej AuthenticationOptions miały zostać ustawione w jednym schemacie uwierzytelniania. Nie było dobrego sposobu, aby to wymusić.

W wersji 2.0 te dwie właściwości zostały usunięte jako właściwości w pojedynczym AuthenticationOptions wystąpieniu. Można je skonfigurować w wywołaniu AddAuthentication metody w ConfigureServices metodzie w metodzie Startup.cs:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);

W poprzednim fragmencie kodu domyślny schemat jest ustawiony na CookieAuthenticationDefaults.AuthenticationScheme ("Cookies").

Alternatywnie użyj przeciążonej AddAuthentication wersji metody, aby ustawić więcej niż jedną właściwość. W poniższym przykładzie przeciążonej metody domyślny schemat jest ustawiony na CookieAuthenticationDefaults.AuthenticationScheme. Schemat uwierzytelniania można również określić w ramach poszczególnych [Authorize] atrybutów lub zasad autoryzacji.

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

Zdefiniuj schemat domyślny w wersji 2.0, jeśli spełniony jest jeden z następujących warunków:

  • Chcesz, aby użytkownik był automatycznie zalogowany
  • Używasz atrybutu [Authorize] lub zasad autoryzacji bez określania schematów

Wyjątkiem od tej reguły jest AddIdentity metoda . Ta metoda dodaje cookies dla Ciebie i ustawia domyślne schematy uwierzytelniania i wyzwania do aplikacji cookieIdentityConstants.ApplicationScheme. Ponadto ustawia domyślny schemat logowania na zewnętrzny cookieIdentityConstants.ExternalScheme.

Używanie rozszerzeń uwierzytelniania HttpContext

Interfejs IAuthenticationManager jest głównym punktem wejścia do systemu uwierzytelniania 1.x. Został zastąpiony nowym zestawem HttpContext metod rozszerzeń w Microsoft.AspNetCore.Authentication przestrzeni nazw.

Na przykład projekty 1.x odwołują się do Authentication właściwości:

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

W projektach 2.0 zaimportuj Microsoft.AspNetCore.AuthenticationAuthentication przestrzeń nazw i usuń odwołania do właściwości:

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

Uwierzytelnianie systemu Windows (HTTP.sys / IISIntegration)

Istnieją dwie odmiany uwierzytelniania systemu Windows:

  • Host zezwala tylko na uwierzytelnionych użytkowników. Ta odmiana nie ma wpływu na zmiany w wersji 2.0.

  • Host zezwala zarówno na użytkowników anonimowych, jak i uwierzytelnionych. Ta odmiana ma wpływ na zmiany w wersji 2.0. Na przykład aplikacja powinna zezwalać anonimowym użytkownikom na warstwie IIS lub HTTP.sys , ale autoryzować użytkowników na poziomie kontrolera. W tym scenariuszu ustaw schemat domyślny w metodzie Startup.ConfigureServices .

    W przypadku wartości Microsoft.AspNetCore.Server.IISIntegration ustaw schemat domyślny na IISDefaults.AuthenticationScheme:

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

    W przypadku elementu Microsoft.AspNetCore.Server.HttpSys ustaw schemat domyślny na HttpSysDefaults.AuthenticationScheme:

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

    Nie można ustawić schematu domyślnego uniemożliwia pracę żądania autoryzacji (wyzwanie) z następującym wyjątkiem:

    System.InvalidOperationException: nie określono parametru authenticationScheme i nie znaleziono elementu DefaultChallengeScheme.

Aby uzyskać więcej informacji, zobacz Konfigurowanie uwierzytelniania systemu Windows w ASP.NET Core.

IdentityCookieWystąpienia opcji

Efektem ubocznym zmian w wersji 2.0 jest przejście na używanie nazwanych opcji zamiast cookie wystąpień opcji. Możliwość dostosowywania nazw schematów Identitycookie jest usuwana.

Na przykład projekty 1.x używają iniekcji konstruktora do przekazywania parametru IdentityCookieOptions do AccountController.cs i ManageController.cs. Dostęp do schematu uwierzytelniania zewnętrznego cookie jest uzyskiwany z podanego wystąpienia:

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

Wyżej wymienione wstrzykiwanie konstruktora staje się niepotrzebne w projektach 2.0, a _externalCookieScheme pole można usunąć:

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

Projekty 1.x używały _externalCookieScheme pola w następujący sposób:

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

W projektach 2.0 zastąp powyższy kod poniższym kodem. Stała IdentityConstants.ExternalScheme może być używana bezpośrednio.

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

Rozwiąż nowo dodane SignOutAsync wywołanie, importując następującą przestrzeń nazw:

using Microsoft.AspNetCore.Authentication;

Dodawanie Identitywłaściwości nawigacji poco użytkownika

Właściwości nawigacji podstawowego IdentityUser modelu POCO (Zwykły stary obiekt CLR) platformy Entity Framework (EF) zostały usunięte. Jeśli projekt 1.x używał tych właściwości, ręcznie dodaj je z powrotem do projektu 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>>();

Aby zapobiec duplikowaniu kluczy obcych podczas uruchamiania EF Core funkcji Migrations, dodaj następujący kod do IdentityDbContext metody klasy OnModelCreating (po wywołaniu base.OnModelCreating(); ):

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

Zastąp element GetExternalAuthenticationSchemes

Metoda GetExternalAuthenticationSchemes synchroniczna została usunięta na rzecz wersji asynchronicznej. Projekty 1.x mają następujący kod w pliku Controllers/ManageController.cs:

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

Ta metoda jest również wyświetlana w Views/Account/Login.cshtml pliku :

@{
    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>
    }
}

W projektach 2.0 użyj GetExternalAuthenticationSchemesAsync metody . Zmiana w pliku ManageController.cs przypomina następujący kod:

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

W Login.cshtmlpliku AuthenticationScheme właściwość dostępna w foreach pętli zmienia się na 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>
    }
}

Zmiana właściwości ManageLoginsViewModel

Obiekt ManageLoginsViewModel jest używany w ManageLogins akcji .ManageController.cs W projektach 1.x zwracany OtherLogins typ właściwości obiektu to IList<AuthenticationDescription>. Ten typ zwracany wymaga zaimportowania elementu 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; }
    }
}

W projektach 2.0 zwracany typ zmienia się na IList<AuthenticationScheme>. Ten nowy typ zwracany Microsoft.AspNetCore.Http.Authentication wymaga zastąpienia importu importem 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; }
    }
}

Dodatkowe zasoby

Aby uzyskać więcej informacji, zobacz temat Dyskusja dotycząca problemu z uwierzytelnianiem 2.0 w witrynie GitHub.