Migrasikan autentikasi dan Identity ke ASP.NET Core 2.0

Oleh Scott Addie dan Hao Kung

ASP.NET Core 2.0 memiliki model baru untuk autentikasi dan Identity yang menyederhanakan konfigurasi dengan menggunakan layanan. ASP.NET aplikasi Core 1.x yang menggunakan autentikasi atau Identity dapat diperbarui untuk menggunakan model baru seperti yang diuraikan di bawah ini.

Memperbarui namespace layanan

Dalam 1.x, kelas seperti IdentityRole itu dan IdentityUser ditemukan di Microsoft.AspNetCore.Identity.EntityFrameworkCore namespace layanan.

Pada 2.0, Microsoft.AspNetCore.Identity namespace menjadi rumah baru untuk beberapa kelas tersebut. Dengan kode default Identity , kelas yang terpengaruh termasuk ApplicationUser dan Startup. Sesuaikan pernyataan Anda using untuk menyelesaikan referensi yang terpengaruh.

Middleware dan layanan Autentikasi

Dalam proyek 1.x, autentikasi dikonfigurasi melalui middleware. Metode middleware dipanggil untuk setiap skema autentikasi yang ingin Anda dukung.

Contoh 1.x berikut mengonfigurasi autentikasi Facebook dengan Identity di 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"]
    });
}

Dalam proyek 2.0, autentikasi dikonfigurasi melalui layanan. Setiap skema autentikasi terdaftar dalam ConfigureServices metode Startup.cs. Metode UseIdentity ini diganti dengan UseAuthentication.

Contoh 2.0 berikut mengonfigurasi autentikasi Facebook dengan Identity di 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();
}

Metode ini UseAuthentication menambahkan satu komponen middleware autentikasi, yang bertanggung jawab atas autentikasi otomatis dan penanganan permintaan autentikasi jarak jauh. Ini menggantikan semua komponen middleware individual dengan satu komponen middleware umum.

Di bawah ini adalah instruksi migrasi 2.0 untuk setiap skema autentikasi utama.

Pilih salah satu dari dua opsi di bawah ini, dan buat perubahan yang diperlukan di Startup.cs:

  1. Gunakan cookies dengan Identity

    • Ganti UseIdentity dengan UseAuthentication dalam Configure metode :

      app.UseAuthentication();
      
    • Panggil AddIdentity metode dalam ConfigureServices metode untuk menambahkan cookie layanan autentikasi.

    • Secara opsional, panggil ConfigureApplicationCookie metode atau ConfigureExternalCookie dalam ConfigureServices metode untuk mengubah Identitycookie pengaturan.

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

    • UseCookieAuthentication Ganti panggilan metode dalam Configure metode dengan UseAuthentication:

      app.UseAuthentication();
      
    • Panggil AddAuthentication metode dan AddCookie dalam ConfigureServices metode :

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

Autentikasi Pembawa JWT

Buat perubahan berikut di Startup.cs:

  • UseJwtBearerAuthentication Ganti panggilan metode dalam Configure metode dengan UseAuthentication:

    app.UseAuthentication();
    
  • Panggil AddJwtBearer metode dalam ConfigureServices metode :

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

    Cuplikan kode ini tidak menggunakan Identity, sehingga skema default harus diatur dengan meneruskan JwtBearerDefaults.AuthenticationScheme ke AddAuthentication metode .

Autentikasi openID Koneksi (OIDC)

Buat perubahan berikut di Startup.cs:

  • UseOpenIdConnectAuthentication Ganti panggilan metode dalam Configure metode dengan UseAuthentication:

    app.UseAuthentication();
    
  • Panggil AddOpenIdConnect metode dalam ConfigureServices metode :

    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 Ganti properti dalam OpenIdConnectOptions tindakan dengan SignedOutRedirectUri:

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

Autentikasi Facebook

Buat perubahan berikut di Startup.cs:

  • UseFacebookAuthentication Ganti panggilan metode dalam Configure metode dengan UseAuthentication:

    app.UseAuthentication();
    
  • Panggil AddFacebook metode dalam ConfigureServices metode :

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

Autentikasi Google

Buat perubahan berikut di Startup.cs:

  • UseGoogleAuthentication Ganti panggilan metode dalam Configure metode dengan UseAuthentication:

    app.UseAuthentication();
    
  • Panggil AddGoogle metode dalam ConfigureServices metode :

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

Autentikasi Akun Microsoft

Untuk informasi selengkapnya tentang autentikasi akun Microsoft, lihat masalah GitHub ini.

Buat perubahan berikut di Startup.cs:

  • UseMicrosoftAccountAuthentication Ganti panggilan metode dalam Configure metode dengan UseAuthentication:

    app.UseAuthentication();
    
  • Panggil AddMicrosoftAccount metode dalam ConfigureServices metode :

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

Autentikasi Twitter

Buat perubahan berikut di Startup.cs:

  • UseTwitterAuthentication Ganti panggilan metode dalam Configure metode dengan UseAuthentication:

    app.UseAuthentication();
    
  • Panggil AddTwitter metode dalam ConfigureServices metode :

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

Mengatur skema autentikasi default

Dalam 1.x, AutomaticAuthenticate properti dan AutomaticChallenge kelas AuthenticationOptions dasar dimaksudkan untuk diatur pada skema autentikasi tunggal. Tidak ada cara yang baik untuk menegakkan ini.

Dalam 2.0, kedua properti ini telah dihapus sebagai properti pada instans individual AuthenticationOptions . Mereka dapat dikonfigurasi dalam AddAuthentication panggilan metode dalam ConfigureServices metode :Startup.cs

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);

Dalam cuplikan kode sebelumnya, skema default diatur ke CookieAuthenticationDefaults.AuthenticationScheme ("Cookies").

Atau, gunakan versi AddAuthentication metode yang kelebihan beban untuk mengatur lebih dari satu properti. Dalam contoh metode kelebihan beban berikut, skema default diatur ke CookieAuthenticationDefaults.AuthenticationScheme. Skema autentikasi dapat ditentukan secara alternatif dalam atribut atau kebijakan otorisasi individual [Authorize] Anda.

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

Tentukan skema default di 2.0 jika salah satu kondisi berikut ini benar:

  • Anda ingin pengguna masuk secara otomatis
  • Anda menggunakan [Authorize] atribut atau kebijakan otorisasi tanpa menentukan skema

Pengecualian untuk aturan ini adalah AddIdentity metode . Metode ini menambahkan cookies untuk Anda dan mengatur skema autentikasi dan tantangan default ke aplikasi cookieIdentityConstants.ApplicationScheme. Selain itu, ini mengatur skema masuk default ke eksternal cookieIdentityConstants.ExternalScheme.

Menggunakan ekstensi autentikasi HttpContext

Antarmuka IAuthenticationManager adalah titik masuk utama ke dalam sistem autentikasi 1.x. Ini telah diganti dengan sekumpulan HttpContext metode ekstensi baru di Microsoft.AspNetCore.Authentication namespace layanan.

Misalnya, proyek 1.x mereferensikan Authentication properti:

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

Dalam proyek 2.0, impor Microsoft.AspNetCore.Authentication namespace layanan, dan hapus Authentication referensi properti:

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

Autentikasi Windows (HTTP.sys / IISIntegration)

Ada dua variasi autentikasi Windows:

  • Host hanya mengizinkan pengguna yang diautentikasi. Variasi ini tidak terpengaruh oleh perubahan 2.0.

  • Host memungkinkan pengguna anonim dan terautentikasi. Variasi ini dipengaruhi oleh perubahan 2.0. Misalnya, aplikasi harus mengizinkan pengguna anonim di lapisan IIS atau HTTP.sys tetapi mengotorisasi pengguna di tingkat pengontrol. Dalam skenario ini, atur skema default dalam Startup.ConfigureServices metode .

    Untuk Microsoft.AspNetCore.Server.IISIntegration, atur skema default ke IISDefaults.AuthenticationScheme:

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

    Untuk Microsoft.AspNetCore.Server.HttpSys, atur skema default ke HttpSysDefaults.AuthenticationScheme:

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

    Kegagalan untuk mengatur skema default mencegah permintaan otorisasi (tantangan) bekerja dengan pengecualian berikut:

    System.InvalidOperationException: Tidak ada authenticationScheme yang ditentukan, dan tidak ada DefaultChallengeScheme yang ditemukan.

Untuk informasi selengkapnya, lihat Mengonfigurasi Autentikasi Windows di ASP.NET Core.

IdentityCookieInstans opsi

Efek samping dari perubahan 2.0 adalah beralih menggunakan opsi bernama alih-alih cookie instans opsi. Kemampuan untuk menyesuaikan Identitycookie nama skema dihapus.

Misalnya, proyek 1.x menggunakan injeksi konstruktor untuk meneruskan IdentityCookieOptions parameter ke dalam AccountController.cs dan ManageController.cs. Skema autentikasi eksternal cookie diakses dari instans yang disediakan:

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

Injeksi konstruktor yang disebutkan di atas menjadi tidak perlu dalam proyek 2.0, dan _externalCookieScheme bidang dapat dihapus:

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

Proyek 1.x menggunakan _externalCookieScheme bidang sebagai berikut:

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

Dalam proyek 2.0, ganti kode sebelumnya dengan yang berikut ini. Konstanta IdentityConstants.ExternalScheme dapat digunakan secara langsung.

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

Atasi panggilan yang baru ditambahkan SignOutAsync dengan mengimpor namespace berikut:

using Microsoft.AspNetCore.Authentication;

Menambahkan Identityproperti navigasi POCO Pengguna

Properti navigasi Core Entity Framework (EF) dari POCO dasar IdentityUser (Objek CLR Lama Biasa) telah dihapus. Jika proyek 1.x Anda menggunakan properti ini, tambahkan kembali secara manual ke proyek 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>>();

Untuk mencegah kunci asing duplikat saat menjalankan EF Core Migrasi, tambahkan yang berikut ini ke metode kelas OnModelCreating Anda IdentityDbContext (setelah base.OnModelCreating(); panggilan):

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

Mengganti GetExternalAuthenticationSchemes

Metode sinkron GetExternalAuthenticationSchemes dihapus demi versi asinkron. Proyek 1.x memiliki kode berikut di Controllers/ManageController.cs:

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

Metode ini juga muncul di 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>
    }
}

Dalam proyek 2.0, gunakan metode .GetExternalAuthenticationSchemesAsync Perubahan menyerupan ManageController.cs kode berikut:

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

Dalam Login.cshtml, properti yang AuthenticationScheme diakses dalam perulangan foreach berubah menjadi 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>
    }
}

Perubahan properti ManageLoginsViewModel

Objek ManageLoginsViewModel digunakan dalam ManageLogins tindakan ManageController.cs. Dalam proyek 1.x, jenis pengembalian properti objek OtherLogins adalah IList<AuthenticationDescription>. Jenis pengembalian ini memerlukan impor 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; }
    }
}

Dalam proyek 2.0, jenis pengembalian berubah menjadi IList<AuthenticationScheme>. Jenis pengembalian baru ini memerlukan mengganti Microsoft.AspNetCore.Http.Authentication impor dengan Microsoft.AspNetCore.Authentication impor.

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

Sumber Daya Tambahan:

Untuk informasi selengkapnya, lihat masalah Diskusi untuk Auth 2.0 di GitHub.