將驗證和 Identity 移轉至 ASP.NET Core 2.0

作者 :Scott Addie,Kung

ASP.NET Core 2.0 具有新的驗證模型,可 Identity 簡化使用服務的設定。 ASP.NET Core使用驗證或 Identity 更新的 1.x 應用程式,以使用新的模型,如下所述。

更新命名空間

在 1.x 中,在 命名空間中找到這類 IdentityRoleIdentityUser 類別 Microsoft.AspNetCore.Identity.EntityFrameworkCore

在 2.0 中 Microsoft.AspNetCore.Identity ,命名空間成為數個這類類別的新首頁。 使用預設 Identity 程式碼時,受影響的類別包括 ApplicationUserStartup 。 調整語句 using 以解析受影響的參考。

驗證中介軟體和服務

在 1.x 專案中,驗證是透過中介軟體進行設定。 系統會針對您想要支援的每個驗證配置叫用中介軟體方法。

下列 1.x 範例會在 中 Startup.cs 設定 Identity Facebook 驗證:

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 專案中,會透過服務設定驗證。 每個驗證配置都會在 的 Startup.cs 方法中 ConfigureServices 註冊。 方法 UseIdentity 會取代為 UseAuthentication

下列 2.0 範例會在 中 Startup.cs 設定 Identity Facebook 驗證:

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. 搭配使用 cookie s Identity

    • 將 取代 UseIdentityUseAuthentication 方法中的 Configure

      app.UseAuthentication();
      
    • AddIdentity叫用 方法中的 ConfigureServices 方法,以新增 cookie 驗證服務。

    • 或者,在 方法中 ConfigureServicesConfigureApplicationCookie 用 或 ConfigureExternalCookie 方法,以調整 Identitycookie 設定。

      services.AddIdentity<ApplicationUser, IdentityRole>()
              .AddEntityFrameworkStores<ApplicationDbContext>()
              .AddDefaultTokenProviders();
      
      services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
      
  2. 不使用 cookieIdentity

    • UseCookieAuthentication 方法中的 Configure 方法呼叫取代為 UseAuthentication

      app.UseAuthentication();
      
    • AddAuthentication 方法中 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";
              });
      

JWT 持有人驗證

Startup.cs 中進行下列變更:

  • UseJwtBearerAuthentication 方法中的 Configure 方法呼叫取代為 UseAuthentication

    app.UseAuthentication();
    
  • AddJwtBearer 方法中叫用 ConfigureServices 方法:

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

    此程式碼片段不會使用 Identity ,因此預設配置應該藉由傳遞 JwtBearerDefaults.AuthenticationSchemeAddAuthentication 方法來設定。

OpenID Connect (OIDC) 驗證

Startup.cs 中進行下列變更:

  • UseOpenIdConnectAuthentication 方法中的 Configure 方法呼叫取代為 UseAuthentication

    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 動作中的 OpenIdConnectOptions 屬性取代為 SignedOutRedirectUri

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

Facebook 驗證

Startup.cs 中進行下列變更:

  • UseFacebookAuthentication 方法中的 Configure 方法呼叫取代為 UseAuthentication

    app.UseAuthentication();
    
  • AddFacebook 方法中叫用 ConfigureServices 方法:

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

Google 驗證

Startup.cs 中進行下列變更:

  • UseGoogleAuthentication 方法中的 Configure 方法呼叫取代為 UseAuthentication

    app.UseAuthentication();
    
  • AddGoogle 方法中叫用 ConfigureServices 方法:

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

Microsoft 帳戶驗證

如需 Microsoft 帳戶驗證的詳細資訊,請參閱 此 GitHub 問題

Startup.cs 中進行下列變更:

  • UseMicrosoftAccountAuthentication 方法中的 Configure 方法呼叫取代為 UseAuthentication

    app.UseAuthentication();
    
  • AddMicrosoftAccount 方法中叫用 ConfigureServices 方法:

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

Twitter 驗證

Startup.cs 中進行下列變更:

  • UseTwitterAuthentication 方法中的 Configure 方法呼叫取代為 UseAuthentication

    app.UseAuthentication();
    
  • AddTwitter 方法中叫用 ConfigureServices 方法:

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

設定預設驗證配置

在 1.x 中, AutomaticAuthenticate 基類的 AuthenticationOptionsAutomaticChallenge 屬性是要在單一驗證配置上設定。 沒有好方法可以強制執行此動作。

在 2.0 中,這兩個屬性已移除為個別 AuthenticationOptions 實例上的屬性。 它們可以在 AddAuthentication 的 方法 Startup.cs 呼叫中 ConfigureServices 設定:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);

在上述程式碼片段中,預設配置會設定為 CookieAuthenticationDefaults.AuthenticationScheme (「s」 Cookie ) 。

或者,使用 方法的多 AddAuthentication 載版本來設定多個屬性。 在下列多載方法範例中,預設配置會設定為 CookieAuthenticationDefaults.AuthenticationScheme 。 您也可以在個別 [Authorize] 屬性或授權原則內指定驗證配置。

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

如果下列其中一個條件成立,請在 2.0 中定義預設配置:

  • 您希望使用者自動登入
  • 您不需要指定配置,即可 [Authorize] 使用屬性或授權原則

這個規則的例外狀況是 AddIdentity 方法。 這個方法會為您新增 cookie ,並將預設的驗證和挑戰配置設定為應用程式 cookieIdentityConstants.ApplicationScheme 。 此外,它會將預設登入配置設定為外部 cookieIdentityConstants.ExternalScheme

使用 HttpCoNtext 驗證延伸模組

介面 IAuthenticationManager 是 1.x 驗證系統的主要進入點。 它已被 命名空間中的 Microsoft.AspNetCore.Authentication 一組 HttpContext 新擴充方法取代。

例如,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 變更的影響。 例如,應用程式應該允許 IISHTTP.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:未指定 authenticationScheme,而且找不到 DefaultChallengeScheme。

如需詳細資訊,請參閱在 ASP.NET Core 中設定 Windows 驗證

IdentityCookie選項實例

2.0 變更的副作用是切換為使用具名選項,而不是 cookie 選項實例。 移除自訂 Identitycookie 配置名稱的能力。

例如,1.x 專案會使用 建構函式插入 將參數傳遞 IdentityCookieOptionsAccountController.csManageController.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 使用者 POCO 流覽屬性

已移除基底 IdentityUser POCO (純舊 CLR 物件) 的 Entity Framework (EF) Core 導覽屬性。 如果您的 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 防止重複的外鍵,請在呼叫) 之後 base.OnModelCreating(); ,將下列內容新增至類別 IdentityDbContextOnModelCreating 的 方法 (:

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 ,迴圈中 foreach 存取的屬性會變更為 NameAuthenticationScheme

@{
    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物件用於 ManageLoginsManageController.cs 動作中。 在 1.x 專案中,物件的屬性傳 OtherLogins 回類型為 IList<AuthenticationDescription> 。 此傳回型別需要 的匯入 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.AuthenticationMicrosoft.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; }
    }
}

其他資源

如需詳細資訊,請參閱 GitHub 上的 Auth 2.0 問題討論