Миграция проверки подлинности и Identity ASP.NET Core 2.0

Скотт Адди и Хао Кунг

ASP.NET Core 2.0 имеет новую модель для проверки подлинности и Identity упрощает настройку с помощью служб. ASP.NET приложения Core 1.x, использующие проверку подлинности или Identity можно обновить для использования новой модели, как описано ниже.

Обновление пространств имен

В версии 1.x такие классы IdentityRoleIdentityUser были найдены в Microsoft.AspNetCore.Identity.EntityFrameworkCore пространстве имен.

В 2.0 Microsoft.AspNetCore.Identity пространство имен стало новым домом для нескольких таких классов. В коде по умолчанию Identity затронутые классы включают ApplicationUser и Startup. using Настройте инструкции для разрешения затронутых ссылок.

ПО промежуточного слоя проверки подлинности и служб

В проектах 1.x проверка подлинности настраивается с помощью ПО промежуточного слоя. Метод ПО промежуточного слоя вызывается для каждой схемы проверки подлинности, которую требуется поддерживать.

Следующий пример 1.x настраивает проверку подлинности Facebook с помощью IdentityStartup.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"]
    });
}

В проектах 2.0 проверка подлинности настраивается через службы. Каждая схема проверки подлинности регистрируется в методе ConfigureServicesStartup.cs. Метод UseIdentity заменяется UseAuthenticationна .

Следующий пример 2.0 настраивает проверку подлинности Facebook в IdentityStartup.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();
}

Этот UseAuthentication метод добавляет один компонент ПО промежуточного слоя проверки подлинности, который отвечает за автоматическую проверку подлинности и обработку запросов удаленной проверки подлинности. Он заменяет все отдельные компоненты по промежуточного слоя одним общим компонентом по промежуточного слоя.

Ниже приведены инструкции по миграции 2.0 для каждой основной схемы проверки подлинности.

Выберите один из двух следующих вариантов и внесите необходимые изменения в Startup.cs:

  1. Использование cookies с Identity

    • UseAuthentication Замените UseIdentity на Configure метод:

      app.UseAuthentication();
      
    • Вызовите метод в методе AddIdentityConfigureServices , чтобы добавить cookie службы проверки подлинности.

    • При необходимости вызовите ConfigureApplicationCookie метод или ConfigureExternalCookie метод в методе ConfigureServices , чтобы настроить Identitycookie параметры.

      services.AddIdentity<ApplicationUser, IdentityRole>()
              .AddEntityFrameworkStores<ApplicationDbContext>()
              .AddDefaultTokenProviders();
      
      services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
      
  2. Использование cookies без Identity

    • Замените UseCookieAuthentication вызов метода в методе UseAuthenticationследующимConfigure:

      app.UseAuthentication();
      
    • AddAuthentication Вызовите методы и AddCookie методы в методеConfigureServices:

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

Проверка подлинности носителя JWT

Внесите следующие изменения в Startup.cs:

  • Замените UseJwtBearerAuthentication вызов метода в методе UseAuthenticationследующимConfigure:

    app.UseAuthentication();
    
  • AddJwtBearer Вызов метода в методеConfigureServices:

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

    Этот фрагмент кода не используется Identity, поэтому схема по умолчанию должна быть задана путем передачи JwtBearerDefaults.AuthenticationScheme в AddAuthentication метод.

Проверка подлинности Подключение OpenID (OIDC)

Внесите следующие изменения в Startup.cs:

  • Замените UseOpenIdConnectAuthentication вызов метода в методе UseAuthenticationследующимConfigure:

    app.UseAuthentication();
    
  • AddOpenIdConnect Вызов метода в методе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"];
    });
    
  • Замените PostLogoutRedirectUri свойство в действии SignedOutRedirectUriOpenIdConnectOptions следующим:

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

Проверка подлинности Facebook

Внесите следующие изменения в Startup.cs:

  • Замените UseFacebookAuthentication вызов метода в методе UseAuthenticationследующимConfigure:

    app.UseAuthentication();
    
  • AddFacebook Вызов метода в методеConfigureServices:

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

Проверка подлинности Google

Внесите следующие изменения в Startup.cs:

  • Замените UseGoogleAuthentication вызов метода в методе UseAuthenticationследующимConfigure:

    app.UseAuthentication();
    
  • AddGoogle Вызов метода в методеConfigureServices:

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

Проверка подлинности учетной записи Майкрософт

Дополнительные сведения об проверке подлинности учетной записи Майкрософт см . в этой проблеме с GitHub.

Внесите следующие изменения в Startup.cs:

  • Замените UseMicrosoftAccountAuthentication вызов метода в методе UseAuthenticationследующимConfigure:

    app.UseAuthentication();
    
  • AddMicrosoftAccount Вызов метода в методеConfigureServices:

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

Проверка подлинности Twitter

Внесите следующие изменения в Startup.cs:

  • Замените UseTwitterAuthentication вызов метода в методе UseAuthenticationследующимConfigure:

    app.UseAuthentication();
    
  • AddTwitter Вызов метода в методеConfigureServices:

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

Настройка схем проверки подлинности по умолчанию

В версии 1.x AutomaticAuthenticateAutomaticChallengeAuthenticationOptions свойства базового класса должны быть заданы в одной схеме проверки подлинности. Не было хорошего способа применить это.

В версии 2.0 эти два свойства были удалены в качестве свойств в отдельном AuthenticationOptions экземпляре. Их можно настроить в вызове AddAuthentication метода в методе ConfigureServicesStartup.cs:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);

В приведенном выше фрагменте кода для схемы по умолчанию задано значение CookieAuthenticationDefaults.AuthenticationScheme ("Cookies").

Кроме того, используйте перегруженную версию метода для задания нескольких AddAuthentication свойств. В следующем перегруженном примере метода для схемы по умолчанию задано CookieAuthenticationDefaults.AuthenticationSchemeзначение . Схема проверки подлинности может быть также указана в отдельных [Authorize] атрибутах или политиках авторизации.

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

Определите схему по умолчанию в версии 2.0, если одно из следующих условий имеет значение true:

  • Вы хотите, чтобы пользователь автоматически вошел в систему
  • Атрибут или политики авторизации используются [Authorize] без указания схем

Исключением из этого правила является AddIdentity метод. Этот метод добавляет cookieдля вас и задает схемы проверки подлинности и вызовов по умолчанию для приложения cookieIdentityConstants.ApplicationScheme. Кроме того, он задает схему входа по умолчанию во внешнюю cookieIdentityConstants.ExternalScheme.

Использование расширений проверки подлинности HttpContext

Интерфейс IAuthenticationManager является главной точкой входа в систему проверки подлинности 1.x. Он был заменен новым набором HttpContext методов расширения в Microsoft.AspNetCore.Authentication пространстве имен.

Например, проекты 1.x ссылались на Authentication свойство:

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

В проектах 2.0 импортируйте Microsoft.AspNetCore.Authentication пространство имен и удалите ссылки на Authentication свойства:

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

Проверка подлинности Windows (HTTP.sys / IISIntegration)

Существует два варианта проверка подлинности Windows:

  • Узел разрешает только прошедшим проверку подлинности пользователям. Этот вариант не влияет на изменения версии 2.0.

  • Узел разрешает анонимным и прошедшим проверку подлинности пользователям. Этот вариант влияет на изменения версии 2.0. Например, приложение должно разрешить анонимным пользователям на уровне IIS или HTTP.sys , но авторизовать пользователей на уровне контроллера. В этом сценарии задайте схему по умолчанию в методе Startup.ConfigureServices .

    Для Microsoft.AspNetCore.Server.IISIntegration задайте схему IISDefaults.AuthenticationSchemeпо умолчанию следующим образом:

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

    Для Microsoft.AspNetCore.Server.HttpSys задайте схему HttpSysDefaults.AuthenticationSchemeпо умолчанию:

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

    Не удается установить схему по умолчанию, чтобы запретить запрос на авторизацию (вызов) работать со следующим исключением:

    System.InvalidOperationException: не указана проверка подлинностиScheme, и не было найдено DefaultChallengeScheme.

Дополнительные сведения см. в статье Настройка проверки подлинности Windows в ASP.NET Core.

IdentityCookieЭкземпляры параметров

Побочным эффектом изменений версии 2.0 является переход на использование именованных параметров вместо cookie экземпляров параметров. Возможность настраивать Identitycookie имена схем удаляется.

Например, проекты 1.x используют внедрение конструктора для передачи IdentityCookieOptions параметра AccountController.cs в и ManageController.cs. Доступ к внешней cookie схеме проверки подлинности осуществляется из предоставленного экземпляра:

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

Приведенное выше упоминание внедрение конструктора становится ненужным в проектах 2.0, а _externalCookieScheme поле можно удалить:

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 использовали _externalCookieScheme поле следующим образом:

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

В проектах 2.0 замените предыдущий код следующим кодом. Константу IdentityConstants.ExternalScheme можно использовать напрямую.

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

Устраните только что добавленный SignOutAsync вызов, импортируя следующее пространство имен:

using Microsoft.AspNetCore.Authentication;

Добавление Identityсвойств навигации user POCO

Свойства навигации Entity Framework (EF) Core базового IdentityUser POCO (обычный старый объект CLR) были удалены. Если проект 1.x использовал эти свойства, вручную добавьте их в проект 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>>();

Чтобы предотвратить дублирование внешних ключей при выполнении EF Core миграций, добавьте в метод класса IdentityDbContextOnModelCreating следующий код (после 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);
}

Замена GetExternalAuthenticationSchemes

Синхронный метод GetExternalAuthenticationSchemes был удален в пользу асинхронной версии. В проектах 1.x есть следующий код:Controllers/ManageController.cs

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

Этот метод также отображается: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>
    }
}

В проектах 2.0 используйте GetExternalAuthenticationSchemesAsync метод. Это изменение ManageController.cs напоминает следующий код:

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

В Login.cshtml, доступ к свойствуAuthenticationScheme, к который обращается в циклеforeach, изменяется: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>
    }
}

Изменение свойства ManageLoginsViewModel

Объект ManageLoginsViewModel используется в ManageLogins действии ManageController.cs. В проектах 1.x возвращается IList<AuthenticationDescription>тип возвращаемого OtherLogins свойства объекта. Для этого типа возврата требуется импорт 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; }
    }
}

В проектах 2.0 возвращаемый тип изменяется IList<AuthenticationScheme>на . Этот новый тип возвращаемого Microsoft.AspNetCore.Http.Authentication значения требует замены импорта импортом 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; }
    }
}

Дополнительные ресурсы

Дополнительные сведения см. в статье "Обсуждение проверки подлинности 2.0 " на сайте GitHub.