ASP.NET Core 簡介 Identity

作者:Rick Anderson

ASP.NET 核心 Identity :

  • 這是支援使用者介面 (UI) 登入功能的 API。
  • 管理使用者、密碼、設定檔資料、角色、宣告、權杖、電子郵件確認等等。

使用者可以使用儲存在 中的 Identity 登入資訊來建立帳戶,也可以使用外部登入提供者。 支援的外部登入提供者包括 Facebook、Google、Microsoft 帳戶和 Twitter

如需如何全域要求所有使用者進行驗證的資訊,請參閱 要求已驗證的使用者

原始程式碼 Identity 可在 GitHub 上取得。 Scaffold Identity 並檢視產生的檔案,以檢閱與 的 Identity 範本互動。

Identity 通常會使用 SQL Server 資料庫來設定,以儲存使用者名稱、密碼和設定檔資料。 或者,也可以使用另一個永續性存放區,例如 Azure 資料表儲存體。

在本主題中,您將瞭解如何使用 Identity 來註冊、登入和登出使用者。 注意:範本會將使用者名稱和電子郵件視為相同的使用者。 如需建立使用 Identity 之應用程式的詳細指示,請參閱 後續步驟

ASP.NET Core Identity 與 Microsoft 身分識別平臺 無關 。 Microsoft 身分識別平臺為:

  • Azure Active Directory (Azure AD) 開發人員平臺的演進。
  • ASP.NET Core 應用程式中驗證和授權的替代身分識別解決方案。

ASP.NET Core Identity 會將使用者介面 (UI) 登入功能新增至 ASP.NET Core Web 應用程式。 若要保護 Web API 和 SPA,請使用下列其中一項:

Duende Identity Server 是適用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 架構。 Duende Identity Server 會啟用下列安全性功能:

  • 驗證即服務 (AaaS)
  • 多個應用程式類型的單一登入/關閉 (SSO)
  • API 的存取控制
  • 同盟閘道

重要

Duende Software 可能會要求您支付授權費用才能在生產環境中使用 Duende Identity 伺服器。 如需詳細資訊,請參閱從 ASP.NET Core 5.0 移轉至 6.0

如需詳細資訊,請參閱 Duende 伺服器檔(Duende Identity Software 網站)。

檢視或下載範例程式碼 如何下載 )。

使用驗證建立 Web 應用程式

使用個別使用者帳戶建立 ASP.NET Core Web 應用程式專案。

  • 選取 [ASP.NET Core Web 應用程式] 範本。 將專案 命名為 WebApp1 ,其命名空間與專案下載相同。 按一下 [確定]
  • 在 [驗證類型] 輸入中,選取 [個別使用者帳戶]

產生的專案提供 ASP.NET Core Identity 作為 Razor 類別庫 。 類別 IdentityRazor 庫會公開具有 Identity 區域的端點。 例如:

  • / Identity /Account/Login
  • / Identity /Account/Logout
  • / Identity /帳戶/管理

套用移轉

套用移轉來初始化資料庫。

在套件管理員主控台中執行下列命令(PMC):

Update-Database

測試註冊和登入

執行應用程式並註冊使用者。 視螢幕大小而定,您可能需要選取流覽切換按鈕以查看 [註冊 ] 和 [登入 ] 連結。

檢視 Identity 資料庫

  • 從 [ 檢視] 功能表中,選取 [SQL Server 物件總管 ] (SSOX)。
  • 流覽至 (localdb)MSSQLLocalDB(SQL Server 13)。 以滑鼠右鍵按一下 dbo。AspNetUsers > 檢視資料

Contextual menu on AspNetUsers table in SQL Server Object Explorer

設定 Identity 服務

服務會在 中 Program.cs 新增。 典型的模式是依下列順序呼叫方法:

  1. Add{Service}
  2. builder.Services.Configure{Service}
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.Configure<IdentityOptions>(options =>
{
    // Password settings.
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;

    // Lockout settings.
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;

    // User settings.
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
    options.User.RequireUniqueEmail = false;
});

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    options.LoginPath = "/Identity/Account/Login";
    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
    options.SlidingExpiration = true;
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

上述程式碼會 Identity 使用預設選項值進行設定。 服務可透過 相依性插入 提供給應用程式。

Identity 藉由呼叫 UseAuthentication 來啟用 。 UseAuthentication 將驗證 中介軟體 新增至要求管線。

範本產生的應用程式不會使用 授權 app.UseAuthorization 包含 ,以確保應用程式新增授權的順序正確。 UseRoutingUseAuthenticationUseAuthorization 必須依上述程式碼所示的順序呼叫。

如需 的詳細資訊 IdentityOptions ,請參閱 IdentityOptions 應用程式啟動

Scaffold Register、Login、LogOut 和 RegisterConfirmation

Register新增 、 LoginLogOutRegisterConfirmation 檔案。 遵循 Scaffold 身分識別到 Razor 具有授權 指示的專案,以產生本節中顯示的程式碼。

檢查暫存器

當使用者按一下頁面上的 Register [註冊 ] 按鈕時,會 RegisterModel.OnPostAsync 叫用動作。 使用者是在 物件上 _userManager 建立的 CreateAsync(TUser)

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

停用預設帳戶驗證

使用預設範本時,系統會將使用者重新導向至 Account.RegisterConfirmation 可選取連結以確認帳戶的 。 預設值 Account.RegisterConfirmation 僅用於 測試,應該在生產應用程式中停用自動帳戶驗證。

若要要求已確認的帳戶,並防止在註冊時立即登入,請在 中 /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs 設定 DisplayConfirmAccountLink = false

[AllowAnonymous]
public class RegisterConfirmationModel : PageModel
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IEmailSender _sender;

    public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
    {
        _userManager = userManager;
        _sender = sender;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return NotFound($"Unable to load user with email '{email}'.");
        }

        Email = email;
        // Once you add a real email sender, you should remove this code that lets you confirm the account
        DisplayConfirmAccountLink = false;
        if (DisplayConfirmAccountLink)
        {
            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            EmailConfirmationUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);
        }

        return Page();
    }
}

登入

登入表單會在下列情況下顯示:

  • 已選取 [ 登入] 連結。
  • 使用者嘗試存取未獲授權存取 的受限制頁面,或 系統尚未驗證時。

提交 [登入] 頁面上的表單時,會 OnPostAsync 呼叫動作。 PasswordSignInAsync 在 物件上 _signInManager 呼叫 。

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

如需如何做出授權決策的資訊,請參閱 ASP.NET Core 中的授權簡介。

登出

登出 連結會叫用 LogoutModel.OnPost 動作。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

在上述程式碼中,程式碼 return RedirectToPage(); 必須是重新導向,讓瀏覽器執行新的要求,並更新使用者的身分識別。

SignOutAsync 清除儲存在 中的 cookie 使用者宣告。

Post 在 中 Pages/Shared/_LoginPartial.cshtml 指定:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

測試 Identity

預設 Web 專案範本允許匿名存取首頁。 若要測試 Identity ,請新增 [Authorize]

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

如果您已登入,請登出。執行應用程式並選取 Privacy 連結。 系統會將您重新導向至登入頁面。

探索 Identity

若要更詳細地探索 Identity :

Identity 元件

Identity所有相依的 NuGet 套件都包含在 ASP.NET Core 共用架構 中。

的主要套件 Identity 是 Microsoft.AspNetCore. Identity 此套件包含 ASP.NET Core Identity 的核心介面集,且 由 Microsoft.AspNetCore.Identity.EntityFrameworkCore 包含。

移轉至 ASP.NET Core Identity

如需移轉現有 Identity 存放區的詳細資訊和指引,請參閱 移轉驗證和 Identity

設定密碼強度

如需設定最低密碼需求的範例,請參閱 設定。

AddDefault Identity 和 AddIdentity

AddDefaultIdentity 已在 ASP.NET Core 2.1 中引進。 呼叫 AddDefaultIdentity 類似于呼叫下列專案:

如需詳細資訊,請參閱 AddDefault Identity 來源

防止發佈靜態 Identity 資產

若要防止將靜態 Identity 資產(UI 的 Identity 樣式表單和 JavaScript 檔案)發佈至 Web 根目錄,請將下列 ResolveStaticWebAssetsInputsDependsOn 屬性和 RemoveIdentityAssets 目標新增至應用程式的專案檔:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

後續步驟

作者:Rick Anderson

ASP.NET 核心 Identity :

  • 這是支援使用者介面 (UI) 登入功能的 API。
  • 管理使用者、密碼、設定檔資料、角色、宣告、權杖、電子郵件確認等等。

使用者可以使用儲存在 中的 Identity 登入資訊來建立帳戶,也可以使用外部登入提供者。 支援的外部登入提供者包括 Facebook、Google、Microsoft 帳戶和 Twitter

如需如何全域要求所有使用者進行驗證的資訊,請參閱 要求已驗證的使用者

原始程式碼 Identity 可在 GitHub 上取得。 Scaffold Identity 並檢視產生的檔案,以檢閱與 的 Identity 範本互動。

Identity 通常會使用 SQL Server 資料庫來設定,以儲存使用者名稱、密碼和設定檔資料。 或者,也可以使用另一個永續性存放區,例如 Azure 資料表儲存體。

在本主題中,您將瞭解如何使用 Identity 來註冊、登入和登出使用者。 注意:範本會將使用者名稱和電子郵件視為相同的使用者。 如需建立使用 Identity 之應用程式的詳細指示,請參閱 後續步驟

Microsoft 身分識別平臺 為:

  • Azure Active Directory (Azure AD) 開發人員平臺的演進。
  • ASP.NET Core 應用程式中驗證和授權的替代身分識別解決方案。
  • 與 ASP.NET Core Identity 無關。

ASP.NET Core Identity 會將使用者介面 (UI) 登入功能新增至 ASP.NET Core Web 應用程式。 若要保護 Web API 和 SPA,請使用下列其中一項:

Duende Identity Server 是適用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 架構。 Duende Identity Server 會啟用下列安全性功能:

  • 驗證即服務 (AaaS)
  • 多個應用程式類型的單一登入/關閉 (SSO)
  • API 的存取控制
  • 同盟閘道

如需詳細資訊,請參閱 Duende Identity 伺服器 概觀。

如需其他驗證提供者的詳細資訊,請參閱 ASP.NET Core 的社群 OSS 驗證選項

檢視或下載範例程式碼 如何下載 )。

使用驗證建立 Web 應用程式

使用個別使用者帳戶建立 ASP.NET Core Web 應用程式專案。

  • 選取 [檔案]> [新增]> [專案]
  • 選取 [ASP.NET Core Web 應用程式]。 將專案 命名為 WebApp1 ,其命名空間與專案下載相同。 按一下 [確定]
  • 選取 ASP.NET Core Web 應用程式 ,然後選取 [ 變更驗證 ]。
  • 選取 [個別使用者帳戶 ],然後按一下 [ 確定 ]。

產生的專案提供 ASP.NET Core Identity 作為 Razor 類別庫 。 類別 IdentityRazor 庫會公開具有 Identity 區域的端點。 例如:

  • / Identity /Account/Login
  • / Identity /Account/Logout
  • / Identity /帳戶/管理

套用移轉

套用移轉來初始化資料庫。

在套件管理員主控台中執行下列命令(PMC):

PM> Update-Database

測試註冊和登入

執行應用程式並註冊使用者。 視螢幕大小而定,您可能需要選取流覽切換按鈕以查看 [註冊 ] 和 [登入 ] 連結。

檢視 Identity 資料庫

  • 從 [ 檢視] 功能表中,選取 [SQL Server 物件總管 ] (SSOX)。
  • 流覽至 (localdb)MSSQLLocalDB(SQL Server 13)。 以滑鼠右鍵按一下 dbo。AspNetUsers > 檢視資料

Contextual menu on AspNetUsers table in SQL Server Object Explorer

設定 Identity 服務

服務會在 中 ConfigureServices 新增。 典型模式是呼叫所有 Add{Service} 方法,然後呼叫 services.Configure{Service} 方法。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
     // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

上述醒目提示的程式碼會 Identity 使用預設選項值進行設定。 服務可透過 相依性插入 提供給應用程式。

Identity 藉由呼叫 UseAuthentication 來啟用 。 UseAuthentication 將驗證 中介軟體 新增至要求管線。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

上述程式碼會 Identity 使用預設選項值進行設定。 服務可透過 相依性插入 提供給應用程式。

Identity 藉由呼叫 UseAuthentication 來啟用 。 UseAuthentication 將驗證 中介軟體 新增至要求管線。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

範本產生的應用程式不會使用 授權 app.UseAuthorization 包含 ,以確保應用程式新增授權的順序正確。 UseRoutingUseAuthenticationUseAuthorizationUseEndpoints 必須依上述程式碼所示的順序呼叫。

如需 和 的詳細資訊 IdentityOptions ,請參閱 IdentityOptions 應用程式啟動 Startup

Scaffold Register、Login、LogOut 和 RegisterConfirmation

Register新增 、 LoginLogOutRegisterConfirmation 檔案。 遵循 Scaffold 身分識別到 Razor 具有授權 指示的專案,以產生本節中顯示的程式碼。

檢查暫存器

當使用者按一下頁面上的 Register [註冊 ] 按鈕時,會 RegisterModel.OnPostAsync 叫用動作。 使用者是在 物件上 _userManager 建立的 CreateAsync(TUser)

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

停用預設帳戶驗證

使用預設範本時,系統會將使用者重新導向至 Account.RegisterConfirmation 可選取連結以確認帳戶的 。 預設值 Account.RegisterConfirmation 僅用於 測試,應該在生產應用程式中停用自動帳戶驗證。

若要要求已確認的帳戶,並防止在註冊時立即登入,請在 中 /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs 設定 DisplayConfirmAccountLink = false

[AllowAnonymous]
public class RegisterConfirmationModel : PageModel
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IEmailSender _sender;

    public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
    {
        _userManager = userManager;
        _sender = sender;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return NotFound($"Unable to load user with email '{email}'.");
        }

        Email = email;
        // Once you add a real email sender, you should remove this code that lets you confirm the account
        DisplayConfirmAccountLink = false;
        if (DisplayConfirmAccountLink)
        {
            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            EmailConfirmationUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);
        }

        return Page();
    }
}

登入

登入表單會在下列情況下顯示:

  • 已選取 [ 登入] 連結。
  • 使用者嘗試存取未獲授權存取 的受限制頁面,或 系統尚未驗證時。

提交 [登入] 頁面上的表單時,會 OnPostAsync 呼叫動作。 PasswordSignInAsync 在 物件上 _signInManager 呼叫 。

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

如需如何做出授權決策的資訊,請參閱 ASP.NET Core 中的授權簡介。

登出

登出 連結會叫用 LogoutModel.OnPost 動作。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

在上述程式碼中,程式碼 return RedirectToPage(); 必須是重新導向,讓瀏覽器執行新的要求,並更新使用者的身分識別。

SignOutAsync 清除儲存在 中的 cookie 使用者宣告。

Post 在 中 Pages/Shared/_LoginPartial.cshtml 指定:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

測試 Identity

預設 Web 專案範本允許匿名存取首頁。 若要測試 Identity ,請新增 [Authorize]

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

如果您已登入,請登出。執行應用程式並選取 Privacy 連結。 系統會將您重新導向至登入頁面。

探索 Identity

若要更詳細地探索 Identity :

Identity 元件

Identity所有相依的 NuGet 套件都包含在 ASP.NET Core 共用架構 中。

的主要套件 Identity 是 Microsoft.AspNetCore. Identity 此套件包含 ASP.NET Core Identity 的核心介面集,且 由 Microsoft.AspNetCore.Identity.EntityFrameworkCore 包含。

移轉至 ASP.NET Core Identity

如需移轉現有 Identity 存放區的詳細資訊和指引,請參閱 移轉驗證和 Identity

設定密碼強度

如需設定最低密碼需求的範例,請參閱 設定。

AddDefault Identity 和 AddIdentity

AddDefaultIdentity 已在 ASP.NET Core 2.1 中引進。 呼叫 AddDefaultIdentity 類似于呼叫下列專案:

如需詳細資訊,請參閱 AddDefault Identity 來源

防止發佈靜態 Identity 資產

若要防止將靜態 Identity 資產(UI 的 Identity 樣式表單和 JavaScript 檔案)發佈至 Web 根目錄,請將下列 ResolveStaticWebAssetsInputsDependsOn 屬性和 RemoveIdentityAssets 目標新增至應用程式的專案檔:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

後續步驟