ASP.NET Core 中 Identity 的介紹

ASP.NET Core Identity:

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

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

關於如何要求所有應用程式使用者驗證的資訊,請參見 建立一個 ASP.NET Core應用程式,並以授權保護的使用者資料

Identity原始碼可在 GitHub 取得。 建立腳手架 Identity 並檢視產生的檔案,以檢閱範本與 Identity 的互動。

Identity 通常使用 SQL Server 資料庫來儲存使用者名稱、密碼及個人資料。 另外,也可以使用其他持久儲存,例如 Azure 資料表儲存體。

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

如需更多關於 Identity 應用程式中 Blazor 的資訊,請參閱 ASP.NET Core Blazor 認證與授權 以及 Blazor 文件中後續相關文章。

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

  • 這是Azure Active Directory(Azure AD)開發者平台的演進。
  • ASP.NET Core 應用程式中用於認證與授權的替代身份解決方案。

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

Duende Identity Server 是一個用於 ASP.NET Core 的 OpenID Connect 與 OAuth 2.0 框架。 Duende Identity 伺服器會啟用下列安全性功能:

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

Important

Duende Software 可能會要求您支付授權費用才能在生產環境中使用 Duende Identity 伺服器。 更多資訊請參閱 Migrate from ASP.NET Core in .NET 5 至 .NET 6

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

查看或下載範例程式碼how to download)。

使用驗證建立Blazor Web App

建立一個 ASP.NET Core Blazor Web App project,並使用個人帳號。

Note

Razor如需 Pages 體驗,請參閱使用驗證建立 Razor Pages 應用程式一節。

如需MVC體驗,請參閱 使用驗證建立MVC應用程式 一節。

  • 選擇Blazor Web App範本。 選取 下一步
  • 進行下列選擇:
    • 驗證類型個別帳戶
    • 互動式轉譯模式伺服器
    • 互動位置全域
  • 選取 ,創建

產生的專案包含 IdentityRazor 元件。 元件位於伺服器project的 Components/Account 資料夾中。 例如:

  • Components/Account/Pages/Register.razor
  • Components/Account/Pages/Login.razor
  • Components/Account/Pages/Manage/ChangePassword.razor

Identity Razor 在特定使用案例的文件中會個別描述組件,並且每次版本更新時可能會改變。 當你用個人帳戶產生 Blazor Web App 時,產生的 project 會包含 IdentityRazor 元件。 元件也可以在伺服器專案的 資料夾中檢查,這在 專案範本中 ( GitHub 儲存庫)。

Note

指向 .NET 參考來源的文件連結通常會載入儲存庫的預設分支,該分支代表下一版 .NET 版本的當前開發進度。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 欲了解更多資訊,請參閱 如何選擇 ASP.NET Core原始碼版本標籤(dotnet/AspNetCore.Docs #26205)

欲了解更多資訊,請參閱 ASP.NET Core Blazor 認證與授權 以及 Blazor 文件中後續文章。 主 ASP.NET Core文件集中Security 和 Identity 區域的大多數條目都適用於 Blazor 應用程式。 不過,Blazor 檔案集包含的文章和指引具有取代或補充資訊的功能。 我們建議先閱讀一般 ASP.NET Core 文件集,接著存取 BlazorSecurity 與 Identity 文件中的文章。

使用身份驗證建立Razor Pages 應用程式

建立一個 ASP.NET Core 網頁應用程式(Razor 頁面)專案,使用個人帳號。

  • 選擇 ASP.NET Core 網頁應用程式(Razor 頁面) 範本。 選取 下一步
  • 針對 [驗證類型],選取 [個別帳戶]。
  • 選取 ,創建

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

  • Areas/Identity/Pages/Account/Register
  • Areas/Identity/Pages/Account/Login
  • Areas/Identity/Pages/Account/Manage/ChangePassword

特定使用案例的文件中會個別描述頁面,而且每次發行版本時可能會有變更。 欲查看 RCL 中的所有頁面,請參閱 ASP.NET Core 參考來源(dotnet/aspnetcore GitHub 儲存庫,Identity/UI/src/Areas/Identity/Pages 資料夾)。 您可以將單個頁面或所有頁面設定到應用程式中。 更多資訊請參閱Identity。

使用驗證建立MVC應用程式

建立一個 ASP.NET Core MVC project,並使用個人帳號。

  • 選擇 ASP.NET Core Web 應用程式(模型-視圖-控制器) 範本。 選取 下一步
  • 針對 [驗證類型],選取 [個別帳戶]。
  • 選取 ,創建

產生的專案提供ASP.NET Core Identity作為Razor類別庫(RCL)。 類別庫 IdentityRazor 是以頁面 Razor 為基礎,並通過區域 Identity 來公開端點。 例如:

  • Areas/Identity/Pages/Account/Register
  • Areas/Identity/Pages/Account/Login
  • Areas/Identity/Pages/Account/Manage/ChangePassword

特定使用案例的文件中會個別描述頁面,而且每次發行版本時可能會有變更。 欲查看 RCL 中的所有頁面,請參閱 ASP.NET Core 參考來源(dotnet/aspnetcore GitHub 儲存庫,Identity/UI/src/Areas/Identity/Pages 資料夾)。 您可以將單個頁面或所有頁面設定到應用程式中。 更多資訊請參閱Identity。

實施遷移

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

請在 封裝管理員 Console(PMC)執行以下指令:

Update-Database

測試註冊和登入

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

檢視 Identity 資料庫

  • View 選單中,選擇 SQL Server 物件總管 (SSOX)。
  • 請前往 (localdb)MSSQLLocalDB(SQL Server 13)。 以滑鼠右鍵按兩下 dbo。AspNetUsers>檢視數據

在 SQL Server 物件總管中的 AspNetUsers 表格的上下文選單

設定 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 啟用 UseAuthenticationUseAuthentication 將驗證 中間件 新增至要求管線。

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

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

ASP.NET Core Identity 指標

ASP.NET Core Identity 度量提供使用者管理與認證流程的監控功能。 這些指標可協助您偵測可能指出安全性威脅的異常登入模式、追蹤身分識別作業的效能,以及了解使用者如何與驗證功能互動,例如雙因素驗證。 這種可觀察性對於具有嚴格安全要求或經歷高身份驗證流量的應用程式特別有價值。

欲了解可用指標的完整細節及使用方法,請參閱 ASP.NET Core metrics

Scaffold註冊、登入、登出和註冊確認

新增 RegisterLoginLogOutRegisterConfirmation 檔案。 依照 Scaffold 身份進入 Razor project,並附上授權指令,即可產生本節所示的程式碼。

檢查暫存器

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

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僅用於測試,應該在生產應用程式中停用自動帳戶驗證。

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

[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 中的使用者宣告。

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

設定密碼強度

請參閱設定以查看設定最低密碼要求的範例。

AddDefaultIdentity 和 AddIdentity

AddDefaultIdentity 於 ASP.NET Core 2.1 中引入。 呼叫 AddDefaultIdentity 類似於呼叫下列項目:

如需更多資訊,請參見 AddDefaultIdentity source

防止靜態 Identity 資產發佈

為防止將靜態 Identity 資產(Identity UI 的樣式表與 JavaScript 檔案)發佈到 網頁根目錄,請在應用程式的 project 檔案中新增以下 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 Core Identity:

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

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

關於如何要求所有應用程式使用者驗證的資訊,請參見 建立一個 ASP.NET Core應用程式,並以授權保護的使用者資料

Identity原始碼可在 GitHub 取得。 建立腳手架 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 為 ASP.NET Core web apps 新增使用者介面(UI)登入功能。 若要保護 Web API 和 SPA,請使用下列其中一項:

Duende Identity Server 是一個用於 ASP.NET Core 的 OpenID Connect 與 OAuth 2.0 框架。 Duende Identity 伺服器會啟用下列安全性功能:

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

Important

Duende Software 可能會要求您支付授權費用才能在生產環境中使用 Duende Identity 伺服器。 更多資訊請參閱 Migrate from ASP.NET Core in .NET 5 至 .NET 6

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

查看或下載範例程式碼how to download)。

建立具有驗證功能的 Web 應用程式

建立一個包含個人使用者帳號的 ASP.NET Core 網頁應用 project。

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

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

  • /Identity/帳號/登入
  • /Identity/帳號/登出
  • /Identity/Account/管理

實施遷移

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

請在 封裝管理員 Console(PMC)執行以下指令:

Update-Database

測試註冊和登入

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

檢視 Identity 資料庫

  • View 選單中,選擇 SQL Server 物件總管 (SSOX)。
  • 請前往 (localdb)MSSQLLocalDB(SQL Server 13)。 以滑鼠右鍵按兩下 dbo。AspNetUsers>檢視數據

在 SQL Server 物件總管中的 AspNetUsers 表格的上下文選單

設定 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 啟用 UseAuthenticationUseAuthentication 將驗證 中間件 新增至要求管線。

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

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

Scaffold註冊、登入、登出和註冊確認

新增 RegisterLoginLogOutRegisterConfirmation 檔案。 依照 Scaffold 身份進入 Razor project,並附上授權指令,即可產生本節所示的程式碼。

檢查暫存器

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

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僅用於測試,應該在生產應用程式中停用自動帳戶驗證。

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

[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 中的使用者宣告。

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

設定密碼強度

請參閱設定以查看設定最低密碼要求的範例。

AddDefaultIdentity 和 AddIdentity

AddDefaultIdentity 於 ASP.NET Core 2.1 中引入。 呼叫 AddDefaultIdentity 類似於呼叫下列項目:

如需更多資訊,請參見 AddDefaultIdentity source

防止靜態 Identity 資產發佈

為防止將靜態 Identity 資產(Identity UI 的樣式表與 JavaScript 檔案)發佈到 網頁根目錄,請在應用程式的 project 檔案中新增以下 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 Core Identity:

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

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

關於如何要求所有應用程式使用者驗證的資訊,請參見 建立一個 ASP.NET Core應用程式,並以授權保護的使用者資料

Identity原始碼可在 GitHub 取得。 建立腳手架 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 為 ASP.NET Core web apps 新增使用者介面(UI)登入功能。 若要保護 Web API 和 SPA,請使用下列其中一項:

Duende IdentityServer 是一個針對 ASP.NET Core 的 OpenID Connect 與 OAuth 2.0 框架。 Duende IdentityServer 會啟用下列安全性功能:

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

如需詳細資訊,請參閱 Duende IdentityServer 概觀

欲了解更多關於其他身份驗證提供者的資訊,請參閱 ASP.NET Core 的 Community OSS 認證選項

查看或下載範例程式碼how to download)。

建立具有驗證功能的 Web 應用程式

建立一個包含個人使用者帳號的 ASP.NET Core 網頁應用 project。

  • 選擇 File>New>Project
  • 選擇 ASP.NET Core 網頁應用程式。 將 project WebApp1 命名為與project下載相同的命名空間。 按一下確定
  • 選擇一個 ASP.NET Core Web 應用程式,然後選擇 Change Authentication
  • 選取 [個別使用者帳戶],然後按一下 [確定]

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

  • /Identity/帳號/登入
  • /Identity/帳號/登出
  • /Identity/帳戶/管理

實施遷移

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

請在 封裝管理員 Console(PMC)執行以下指令:

PM> Update-Database

測試註冊和登入

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

檢視 Identity 資料庫

  • View 選單中,選擇 SQL Server 物件總管 (SSOX)。
  • 請前往 (localdb)MSSQLLocalDB(SQL Server 13)。 以滑鼠右鍵按兩下 dbo。AspNetUsers>檢視數據

在 SQL Server 物件總管中的 AspNetUsers 表格的上下文選單

設定 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 啟用 UseAuthenticationUseAuthentication 將驗證 中間件 新增至要求管線。

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 啟用 UseAuthenticationUseAuthentication 將驗證 中間件 新增至要求管線。

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 必須依上述程式碼所示的順序呼叫。

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

Scaffold註冊、登入、登出和註冊確認

新增 RegisterLoginLogOutRegisterConfirmation 檔案。 依照 Scaffold 身份進入 Razor project,並附上授權指令,即可產生本節所示的程式碼。

檢查暫存器

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

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僅用於測試,應該在生產應用程式中停用自動帳戶驗證。

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

[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 中的使用者宣告。

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

設定密碼強度

請參閱設定以查看設定最低密碼要求的範例。

防止靜態 Identity 資產發佈

為防止將靜態 Identity 資產(Identity UI 的樣式表與 JavaScript 檔案)發佈到 網頁根目錄,請在應用程式的 project 檔案中新增以下 ResolveStaticWebAssetsInputsDependsOn 屬性及 RemoveIdentityAssets 目標:

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

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

後續步驟