在 ASP.NET Core 项目中搭建 Identity 的基架

作者:Rick Anderson

BlazorIdentity 基架搭建

ASP.NET Core Identity 基架搭建可为 Blazor Web App 和 Blazor Server 应用添加 ASP.NET Core Identity。 在基架将 IdentityRazor 组件添加到应用后,可以自定义组件来满足应用的要求。

尽管基架会生成必要的 C# 代码来将 Identity 架构添加到应用中,但必须使用 Entity Framework (EF) Core 数据库迁移更新项目的数据库才能完成该过程。 本文介绍了迁移数据库所需的步骤。

运行 Identity 基架后检查更改。 建议使用 GitHub 或者其他使用还原更改功能显示文件更改的源代码管理系统。

使用双因素身份验证 (2FA)帐户确认和密码恢复以及具有 Identity 的其他安全功能时,需要提供服务。 设置 Identity 的基架时,不会生成服务或服务存根。 必须手动添加启用这些功能的服务。

Razor Pages 和 MVC Identity 基架搭建

ASP.NET Core 将 ASP.NET Core Identity 作为 Razor 类库 (RCL) 提供。 包含 Identity 的应用程序可以应用基架来有选择地添加 Identity RCL 中包含的源代码。 建议生成源代码,以便修改代码和更改行为。 例如,可以指示基架生成在注册过程中使用的代码。 自定义 Identity 代码将替代 Identity RCL 提供的默认实现。 若要完全控制 UI 而不使用默认 RCL,请参阅创建完整 Identity UI 源部分。

不包含身份验证的应用程序可以应用基架来添加 RCL Identity 包。 可以选择要生成的 Identity 代码。

尽管基架生成了大部分必要的代码,但你需要更新项目才能完成该过程。 本文档介绍完成 Identity 基架更新所需的步骤。

我们建议使用显示文件差异并允许你退出更改的源代码管理系统。 运行 Identity 基架后检查更改。

使用双因素身份验证帐户确认和密码恢复以及具有 Identity 的其他安全功能时,需要提供服务。 设置 Identity 的基架时,不会生成服务或服务存根。 必须手动添加启用这些功能的服务。 例如,请参阅需要确认电子邮件

通常,使用单个帐户创建的应用应创建新的数据上下文。

将 Identity 架构到 Blazor 项目中

本部分适用于 Blazor Web App 和 Blazor Server 应用。

运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Blazor Identity。 选择“添加”按钮。
  • 在“添加 BlazorIdentity”对话框中:
    • 使用加号 (+) 按钮选择或添加数据库上下文类(DbContext 类)。
    • 选择默认为 SQL Server 的数据库提供程序(数据库提供程序)。
    • 使用加号 (+) 按钮选择或添加用户类(用户类)。
    • 选择“添加”按钮。

生成的 Identity 数据库代码需要 EF Core 迁移。 以下步骤说明如何创建迁移并将其应用到数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

客户端 Blazor 应用(独立 Blazor WebAssembly)

客户端 Blazor 应用(独立 Blazor WebAssembly)使用自己的 Identity UI 方法,且不能使用 ASP.NET Core Identity 基架搭建。

有关详细信息,请参阅 Blazor 安全和 Identity 文章

在没有现有授权的情况下将 Identity 基架搭建到 Razor 项目中

安装 Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet 包。

注意

有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。


运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

迁移、UseAuthentication 和布局

生成的 Identity 数据库代码需要 Entity Framework (EF) Core 迁移。 如果尚未生成用于创建 Identity 架构的迁移并将其应用于数据库,请创建迁移并更新数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

如果已创建 Identity 架构,但未应用于数据库,则只需执行用于更新数据库的命令:

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

可以使用以下命令确认应用了 Identity 架构。 该命令的输出包含一个“applied”列,用于显示已应用于数据库的迁移。

在 Visual Studio 包管理器控制台中,执行 Get-Migration

Get-Migration

如果存在多个数据库上下文,请使用 -Context 参数指定上下文。

布局更改

可选:将登录部分 (_LoginPartial) 添加到布局文件中:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebRPnoAuth2Auth</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/WebRPnoAuth2Auth.styles.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">WebRPnoAuth2Auth</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                    <partial name="_LoginPartial" />
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - WebRPnoAuth2Auth - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

在有授权的情况下将 Identity 基架搭建到 Razor 项目中

安装 Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet 包。

注意

有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。


运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

在没有现有授权的情况下将 Identity 基架搭建到 MVC 项目中

安装 Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet 包。

注意

有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。


运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

可选:将登录部分 (_LoginPartial) 添加到 Views/Shared/_Layout.cshtml 文件中:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebRPnoAuth2Auth</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/WebRPnoAuth2Auth.styles.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">WebRPnoAuth2Auth</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                    <partial name="_LoginPartial" />
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - WebRPnoAuth2Auth - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

生成的 Identity 数据库代码需要 Entity Framework (EF) Core 迁移。 如果尚未生成用于创建 Identity 架构的迁移并将其应用于数据库,请创建迁移并更新数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

如果已创建 Identity 架构,但未应用于数据库,则只需执行用于更新数据库的命令:

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

可以使用以下命令确认应用了 Identity 架构。 该命令的输出包含一个“applied”列,用于显示已应用于数据库的迁移。

在 Visual Studio 包管理器控制台中,执行 Get-Migration

Get-Migration

如果存在多个数据库上下文,请使用 -Context 参数指定上下文。

MapRazorPages 添加到 Program.cs,如以下突出显示的代码所示:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMVCauth.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.AddControllersWithViews();

var app = builder.Build();

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

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

app.UseRouting();

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

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();

app.Run();

在有授权的情况下将 Identity 基架搭建到 MVC 项目中

安装 Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet 包。

注意

有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。


运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

创建完整的 Identity UI 源

若要维护 Identity UI 的完全控制,请运行 Identity 基架,并选择“替代所有文件”。

密码配置

如果 Startup.ConfigureServices 中配置了 PasswordOptions,则搭建的 Identity 页面中的 Password 属性可能需要配置 [StringLength] 特性InputModel Password 属性位于以下文件中:

  • Areas/Identity/Pages/Account/Register.cshtml.cs
  • Areas/Identity/Pages/Account/ResetPassword.cshtml.cs

禁用页面

本部分介绍如何禁用注册页面,而该方法可用于禁用任何页面。

禁用用户注册:

  • 搭建 Identity 的基架。 包括 Account.Register、Account.Login 和 Account.RegisterConfirmation。 例如:

    dotnet aspnet-codegenerator identity -dc RPauth.Data.ApplicationDbContext --files "Account.Register;Account.Login;Account.RegisterConfirmation"
    
  • 更新 Areas/Identity/Pages/Account/Register.cshtml.cs 以便用户无法从此终结点注册:

    public class RegisterModel : PageModel
    {
        public IActionResult OnGet()
        {
            return RedirectToPage("Login");
        }
    
        public IActionResult OnPost()
        {
            return RedirectToPage("Login");
        }
    }
    
  • 更新 Areas/Identity/Pages/Account/Register.cshtml 以便与前面的更改保持一致:

    @page
    @model RegisterModel
    @{
        ViewData["Title"] = "Go to Login";
    }
    
    <h1>@ViewData["Title"]</h1>
    
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
    
  • 注释禁止或从 Areas/Identity/Pages/Account/Login.cshtml 中删除注册链接

    @*
    <p>
        <a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
    </p>
    *@
    
  • 更新 Areas/Identity/Pages/Account/RegisterConfirmation 页。

    • 删除 cshtml 文件中的代码和链接。
    • 删除 PageModel 中的确认代码:
    [AllowAnonymous]
      public class RegisterConfirmationModel : PageModel
      {
          public IActionResult OnGet()
          {  
              return Page();
          }
      }
    

使用其他应用添加用户

提供一种在 Web 应用外部添加用户的机制。 用于添加用户的选项包括:

  • 专用的管理 Web 应用。
  • 控制台应用。

下面的代码概述了一种添加用户的方法:

  • 用户列表将读入内存中。
  • 为每个用户生成唯一强密码。
  • 用户已添加到 Identity 数据库。
  • 系统会通知用户并告知其更改密码。
public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                var context = services.GetRequiredService<AppDbCntx>();
                context.Database.Migrate();

                var config = host.Services.GetRequiredService<IConfiguration>();
                var userList = config.GetSection("userList").Get<List<string>>();

                SeedData.Initialize(services, userList).Wait();
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred adding users.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

下面的代码概述了如何添加用户:


public static async Task Initialize(IServiceProvider serviceProvider,
                                    List<string> userList)
{
    var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

    foreach (var userName in userList)
    {
        var userPassword = GenerateSecurePassword();
        var userId = await EnsureUser(userManager, userName, userPassword);

        NotifyUser(userName, userPassword);
    }
}

private static async Task<string> EnsureUser(UserManager<IdentityUser> userManager,
                                             string userName, string userPassword)
{
    var user = await userManager.FindByNameAsync(userName);

    if (user == null)
    {
        user = new IdentityUser(userName)
        {
            EmailConfirmed = true
        };
        await userManager.CreateAsync(user, userPassword);
    }

    return user.Id;
}

对于生产方案,可以遵循类似的方法。

禁止发布静态 Identity 资产

若要防止将静态 Identity 资产发布到 Web 根目录,请参阅 ASP.NET Core 上的 Identity 简介

ASP.NET Core 将 ASP.NET Core Identity 作为 Razor 类库 (RCL) 提供。 包含 Identity 的应用程序可以应用基架来有选择地添加 Identity RCL 中包含的源代码。 建议生成源代码,以便修改代码和更改行为。 例如,可以指示基架生成在注册过程中使用的代码。 生成的代码优先于 Identity RCL 中的相同代码。 若要完全控制 UI 而不使用默认 RCL,请参阅“创建完整 Identity UI 源”部分。

不包含身份验证的应用程序可以应用基架来添加 RCL Identity 包。 可以选择要生成的 Identity 代码。

尽管基架生成了大部分必要的代码,但你需要更新项目才能完成该过程。 本文档介绍完成 Identity 基架更新所需的步骤。

我们建议使用显示文件差异并允许你退出更改的源代码管理系统。 运行 Identity 基架后检查更改。

使用双因素身份验证帐户确认和密码恢复以及具有 Identity 的其他安全功能时,需要提供服务。 设置 Identity 的基架时,不会生成服务或服务存根。 必须手动添加启用这些功能的服务。 例如,请参阅需要确认电子邮件

通常,使用单个帐户创建的应用应创建新的数据上下文。

在没有现有授权的情况下将 Identity 基架搭建到 Razor 项目中

安装 Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet 包。

注意

有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。


运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

迁移、UseAuthentication 和布局

生成的 Identity 数据库代码需要 Entity Framework (EF) Core 迁移。 如果尚未生成用于创建 Identity 架构的迁移并将其应用于数据库,请创建迁移并更新数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

如果已创建 Identity 架构,但未应用于数据库,则只需执行用于更新数据库的命令:

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

可以使用以下命令确认应用了 Identity 架构。 该命令的输出包含一个“applied”列,用于显示已应用于数据库的迁移。

在 Visual Studio 包管理器控制台中,执行 Get-Migration

Get-Migration

如果存在多个数据库上下文,请使用 -Context 参数指定上下文。

布局更改

可选:将登录部分 (_LoginPartial) 添加到布局文件中:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebRPnoAuth2Auth</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/WebRPnoAuth2Auth.styles.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">WebRPnoAuth2Auth</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                    <partial name="_LoginPartial" />
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - WebRPnoAuth2Auth - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

在有授权的情况下将 Identity 基架搭建到 Razor 项目中

安装 Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet 包。

注意

有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。


运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

在没有现有授权的情况下将 Identity 基架搭建到 MVC 项目中

安装 Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet 包。

注意

有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。


运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

可选:将登录部分 (_LoginPartial) 添加到 Views/Shared/_Layout.cshtml 文件中:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebRPnoAuth2Auth</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/WebRPnoAuth2Auth.styles.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">WebRPnoAuth2Auth</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                    <partial name="_LoginPartial" />
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - WebRPnoAuth2Auth - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

生成的 Identity 数据库代码需要 Entity Framework (EF) Core 迁移。 如果尚未生成用于创建 Identity 架构的迁移并将其应用于数据库,请创建迁移并更新数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

如果已创建 Identity 架构,但未应用于数据库,则只需执行用于更新数据库的命令:

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

可以使用以下命令确认应用了 Identity 架构。 该命令的输出包含一个“applied”列,用于显示已应用于数据库的迁移。

在 Visual Studio 包管理器控制台中,执行 Get-Migration

Get-Migration

如果存在多个数据库上下文,请使用 -Context 参数指定上下文。

MapRazorPages 添加到 Program.cs,如以下突出显示的代码所示:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMVCauth.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.AddControllersWithViews();

var app = builder.Build();

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

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

app.UseRouting();

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

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();

app.Run();

在有授权的情况下将 Identity 基架搭建到 MVC 项目中

安装 Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet 包。

注意

有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。


运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

使用授权将 Identity 搭建到服务器端 Blazor 应用中

安装 Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet 包。

注意

有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。


运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

迁移

生成的 Identity 数据库代码需要 Entity Framework (EF) Core 迁移。 如果尚未生成用于创建 Identity 架构的迁移并将其应用于数据库,请创建迁移并更新数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

如果已创建 Identity 架构,但未应用于数据库,则只需执行用于更新数据库的命令:

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

可以使用以下命令确认应用了 Identity 架构。 该命令的输出包含一个“applied”列,用于显示已应用于数据库的迁移。

在 Visual Studio 包管理器控制台中,执行 Get-Migration

Get-Migration

如果存在多个数据库上下文,请使用 -Context 参数指定上下文。

样式身份验证终结点

由于服务器端 Blazor 应用使用 Razor Pages Identity 页,因此当访问者在 Identity 页和组件之间导航时,UI 的样式会发生变化。 可以通过两个选项来处理不合适的样式:

自定义 Identity 组件

ASP.NET Core Identity 设计用于 HTTP 请求和响应通信的上下文中,这不是 Blazor 应用中的主要客户端-服务器通信模型。 将 ASP.NET Core Identity 用于用户管理的 ASP.NET Core 应用应该使用 Razor Pages,而不是 Identity 相关的 UI 的 Razor 组件,例如用户注册、登录、注销和其他用户管理任务。

由于 Razor 组件不支持 SignInManager<TUser>UserManager<TUser>,因此建议使用 Web API 通过启用了服务器端 Identity 的 ASP.NET Core 应用来管理 Razor 组件中的 Identity 操作。 有关为 Blazor 应用创建 Web API 的指南,请参阅从 ASP.NET Core Blazor 应用调用 Web API

将 Razor 组件用于 Identity 而不是 Razor Pages 的方法是生成自己的自定义 IdentityRazor 组件,但 Microsoft 不建议或不支持此方法。 有关其他上下文,请浏览以下讨论。 在以下讨论中,问题注释中的代码示例和非 Microsoft GitHub 存储库中的交叉链接代码示例不受 Microsoft 支持,但可能对一些开发人员有所帮助:

在寻求构建自定义 IdentityRazor 组件或搜索第三方 Razor 组件时,如果需要其他帮助,则建议使用以下资源:

将自定义布局与 Blazor 应用样式结合使用

可以修改 Identity 页面布局和样式,以生成样式类似于默认 Blazor 主题的页面。 本文档未介绍此方法。

客户端 Blazor 应用

客户端 Blazor 应用使用自身的 Identity UI 方法,且不能使用 ASP.NET Core Identity 基架。 托管 Blazor 解决方案的服务器端 ASP.NET Core 应用可以遵循本文中的 Razor Pages/MVC 指南,其配置方式与支持 Identity 的任何其他类型的 ASP.NET Core 应用一样。

Blazor 框架不包括 Identity UI 页面的 Razor 组件版本。 Identity UI Razor 组件可以是自定义生成的,也可以从不受支持的第三方源获取。

有关详细信息,请参阅 Blazor 安全和 Identity 文章

创建完整的 Identity UI 源

若要维护 Identity UI 的完全控制,请运行 Identity 基架,并选择“替代所有文件”。

密码配置

如果 Startup.ConfigureServices 中配置了 PasswordOptions,则搭建的 Identity 页面中的 Password 属性可能需要配置 [StringLength] 特性InputModel Password 属性位于以下文件中:

  • Areas/Identity/Pages/Account/Register.cshtml.cs
  • Areas/Identity/Pages/Account/ResetPassword.cshtml.cs

禁用页面

本部分介绍如何禁用注册页面,而该方法可用于禁用任何页面。

禁用用户注册:

  • 搭建 Identity 的基架。 包括 Account.Register、Account.Login 和 Account.RegisterConfirmation。 例如:

    dotnet aspnet-codegenerator identity -dc RPauth.Data.ApplicationDbContext --files "Account.Register;Account.Login;Account.RegisterConfirmation"
    
  • 更新 Areas/Identity/Pages/Account/Register.cshtml.cs 以便用户无法从此终结点注册:

    public class RegisterModel : PageModel
    {
        public IActionResult OnGet()
        {
            return RedirectToPage("Login");
        }
    
        public IActionResult OnPost()
        {
            return RedirectToPage("Login");
        }
    }
    
  • 更新 Areas/Identity/Pages/Account/Register.cshtml 以便与前面的更改保持一致:

    @page
    @model RegisterModel
    @{
        ViewData["Title"] = "Go to Login";
    }
    
    <h1>@ViewData["Title"]</h1>
    
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
    
  • 注释禁止或从 Areas/Identity/Pages/Account/Login.cshtml 中删除注册链接

    @*
    <p>
        <a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
    </p>
    *@
    
  • 更新 Areas/Identity/Pages/Account/RegisterConfirmation 页。

    • 删除 cshtml 文件中的代码和链接。
    • 删除 PageModel 中的确认代码:
    [AllowAnonymous]
      public class RegisterConfirmationModel : PageModel
      {
          public IActionResult OnGet()
          {  
              return Page();
          }
      }
    

使用其他应用添加用户

提供一种在 Web 应用外部添加用户的机制。 用于添加用户的选项包括:

  • 专用的管理 Web 应用。
  • 控制台应用。

下面的代码概述了一种添加用户的方法:

  • 用户列表将读入内存中。
  • 为每个用户生成唯一强密码。
  • 用户已添加到 Identity 数据库。
  • 系统会通知用户并告知其更改密码。
public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                var context = services.GetRequiredService<AppDbCntx>();
                context.Database.Migrate();

                var config = host.Services.GetRequiredService<IConfiguration>();
                var userList = config.GetSection("userList").Get<List<string>>();

                SeedData.Initialize(services, userList).Wait();
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred adding users.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

下面的代码概述了如何添加用户:


public static async Task Initialize(IServiceProvider serviceProvider,
                                    List<string> userList)
{
    var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

    foreach (var userName in userList)
    {
        var userPassword = GenerateSecurePassword();
        var userId = await EnsureUser(userManager, userName, userPassword);

        NotifyUser(userName, userPassword);
    }
}

private static async Task<string> EnsureUser(UserManager<IdentityUser> userManager,
                                             string userName, string userPassword)
{
    var user = await userManager.FindByNameAsync(userName);

    if (user == null)
    {
        user = new IdentityUser(userName)
        {
            EmailConfirmed = true
        };
        await userManager.CreateAsync(user, userPassword);
    }

    return user.Id;
}

对于生产方案,可以遵循类似的方法。

禁止发布静态 Identity 资产

若要防止将静态 Identity 资产发布到 Web 根目录,请参阅 ASP.NET Core 上的 Identity 简介

ASP.NET Core 将 ASP.NET Core Identity 作为 Razor 类库 (RCL) 提供。 包含 Identity 的应用程序可以应用基架来有选择地添加 Identity RCL 中包含的源代码。 建议生成源代码,以便修改代码和更改行为。 例如,可以指示基架生成在注册过程中使用的代码。 生成的代码优先于 Identity RCL 中的相同代码。 若要完全控制 UI 而不使用默认 RCL,请参阅“创建完整 Identity UI 源”部分。

不包含身份验证的应用程序可以应用基架来添加 RCL Identity 包。 可以选择要生成的 Identity 代码。

尽管基架生成了大部分必要的代码,但你需要更新项目才能完成该过程。 本文档介绍完成 Identity 基架更新所需的步骤。

我们建议使用显示文件差异并允许你退出更改的源代码管理系统。 运行 Identity 基架后检查更改。

使用双因素身份验证帐户确认和密码恢复以及具有 Identity 的其他安全功能时,需要提供服务。 设置 Identity 的基架时,不会生成服务或服务存根。 必须手动添加启用这些功能的服务。 例如,请参阅需要确认电子邮件

使用新数据上下文将 Identity 架构到具有现有个人帐户的项目时,请打开 Startup.ConfigureServices 并删除对以下项的调用:

  • AddDbContext
  • AddDefaultIdentity

例如,在以下代码中注释掉 AddDbContextAddDefaultIdentity

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

前面的代码注释禁止 Areas/Identity/IdentityHostingStartup.cs 中的重复代码

通常,使用单个帐户创建的应用应创建新的数据上下文。

将 Identity 的基架搭建到空项目中

运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

使用类似于以下内容的代码更新 Startup 类:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddRazorPages();
    }

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

        app.UseRouting();

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

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
            endpoints.MapRazorPages();
        });
    }
}

推荐 UseHsts,但不作要求。 有关详细信息,请参阅 HTTP 严格传输安全协议

生成的 Identity 数据库代码需要 Entity Framework (EF) Core 迁移。 如果尚未生成用于创建 Identity 架构的迁移并将其应用于数据库,请创建迁移并更新数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

如果已创建 Identity 架构,但未应用于数据库,则只需执行用于更新数据库的命令:

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

可以使用以下命令确认应用了 Identity 架构。 该命令的输出包含一个“applied”列,用于显示已应用于数据库的迁移。

在 Visual Studio 包管理器控制台中,执行 Get-Migration

Get-Migration

如果存在多个数据库上下文,请使用 -Context 参数指定上下文。

在没有现有授权的情况下将 Identity 基架搭建到 Razor 项目中

运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

Identity 是在 Areas/Identity/IdentityHostingStartup.cs 中配置的。 有关详细信息,请参阅 IHostingStartup

迁移、UseAuthentication 和布局

生成的 Identity 数据库代码需要 Entity Framework (EF) Core 迁移。 如果尚未生成用于创建 Identity 架构的迁移并将其应用于数据库,请创建迁移并更新数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

如果已创建 Identity 架构,但未应用于数据库,则只需执行用于更新数据库的命令:

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

可以使用以下命令确认应用了 Identity 架构。 该命令的输出包含一个“applied”列,用于显示已应用于数据库的迁移。

在 Visual Studio 包管理器控制台中,执行 Get-Migration

Get-Migration

如果存在多个数据库上下文,请使用 -Context 参数指定上下文。

启用身份验证

使用类似于以下内容的代码更新 Startup 类:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

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

推荐 UseHsts,但不作要求。 有关详细信息,请参阅 HTTP 严格传输安全协议

布局更改

可选:将登录部分 (_LoginPartial) 添加到布局文件中:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebRP</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">WebRP</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <partial name="_LoginPartial" />
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2019 - WebRP - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @RenderSection("Scripts", required: false)
</body>
</html>

在有授权的情况下将 Identity 基架搭建到 Razor 项目中

运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

某些 Identity 选项是在 Areas/Identity/IdentityHostingStartup.cs 中配置的。 有关详细信息,请参阅 IHostingStartup

在没有现有授权的情况下将 Identity 基架搭建到 MVC 项目中

运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

可选:将登录部分 (_LoginPartial) 添加到 Views/Shared/_Layout.cshtml 文件中:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - WebRP</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">WebRP</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <partial name="_LoginPartial" />
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2019 - WebRP - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @RenderSection("Scripts", required: false)
</body>
</html>

Pages/Shared/_LoginPartial.cshtml 文件移动到 Views/Shared/_LoginPartial.cshtml

Identity 是在 Areas/Identity/IdentityHostingStartup.cs 中配置的。 有关详细信息,请参阅 IHostingStartup

生成的 Identity 数据库代码需要 Entity Framework (EF) Core 迁移。 如果尚未生成用于创建 Identity 架构的迁移并将其应用于数据库,请创建迁移并更新数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

如果已创建 Identity 架构,但未应用于数据库,则只需执行用于更新数据库的命令:

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

可以使用以下命令确认应用了 Identity 架构。 该命令的输出包含一个“applied”列,用于显示已应用于数据库的迁移。

在 Visual Studio 包管理器控制台中,执行 Get-Migration

Get-Migration

如果存在多个数据库上下文,请使用 -Context 参数指定上下文。

使用类似于以下内容的代码更新 Startup 类:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddRazorPages();
    }

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

        app.UseRouting();

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

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
            endpoints.MapRazorPages();
        });
    }
}

推荐 UseHsts,但不作要求。 有关详细信息,请参阅 HTTP 严格传输安全协议

在有授权的情况下将 Identity 基架搭建到 MVC 项目中

运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

在没有现有授权的情况下将 Identity 搭建到服务器端 Blazor 应用中

运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

Identity 是在 Areas/Identity/IdentityHostingStartup.cs 中配置的。 有关详细信息,请参阅 IHostingStartup

迁移

生成的 Identity 数据库代码需要 Entity Framework (EF) Core 迁移。 如果尚未生成用于创建 Identity 架构的迁移并将其应用于数据库,请创建迁移并更新数据库。

Visual Studio 连接的服务用于添加 EF Core 迁移和更新数据库。

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“添加迁移”。

为迁移提供迁移名称,例如 CreateIdentitySchema,这是描述迁移的名称。 等待数据库上下文加载到“DbContext 类名”字段,这可能需要几秒钟时间。 选择“完成”以创建迁移。

操作完成后,选择“关闭”按钮。

再次选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

更新数据库命令执行尚未在基架创建的迁移代码文件中应用的 Up 方法迁移。 在这种情况下,该命令在 Migrations/{TIME STAMP}_{MIGRATION NAME}.cs 文件中执行 Up 方法,该方法会创建 Identity 表、约束和索引。 {TIME STAMP} 占位符是一个时间戳,{MIGRATION NAME} 占位符是迁移名称。

如果已创建 Identity 架构,但未应用于数据库,则只需执行用于更新数据库的命令:

解决方案资源管理器中,双击“连接的服务”。 在“服务依赖项”的“SQL Server Express LocalDB”区域中,选择省略号 (...),然后选择“更新数据库”命令。

此时会打开“使用最新迁移更新数据库”对话框。 等待“DbContext 类名”字段更新,并加载之前的迁移,这可能需要几秒钟时间。 选择“完成”按钮。

操作完成后,选择“关闭”按钮。

可以使用以下命令确认应用了 Identity 架构。 该命令的输出包含一个“applied”列,用于显示已应用于数据库的迁移。

在 Visual Studio 包管理器控制台中,执行 Get-Migration

Get-Migration

如果存在多个数据库上下文,请使用 -Context 参数指定上下文。

样式身份验证终结点

由于服务器端 Blazor 应用使用 Razor Pages Identity 页,因此当访问者在 Identity 页和组件之间导航时,UI 的样式会发生变化。 可以通过两个选项来处理不合适的样式:

自定义 Identity 组件

将组件用于 Identity 而不是页面的一种方法是生成 Identity 组件。 由于 Razor 组件不支持 SignInManagerUserManager,因此请使用 Blazor 应用中的 Web API 终结点来处理用户帐户操作。

将自定义布局与 Blazor 应用样式结合使用

可以修改 Identity 页面布局和样式,以生成样式类似于默认 Blazor 主题的页面。 本文档未介绍此方法。

使用授权将 Identity 搭建到服务器端 Blazor 应用中

运行 Identity 基架:

  • 在解决方案资源管理器中,右键单击项目 >“添加”>“已搭建基架的新项”。
  • 在“添加已搭建基架的新项”对话框的左侧窗格中,选择 Identity。 在中心窗格中选择 Identity。 选择“添加”按钮。
  • 在“添加 Identity”对话框中,选择想要的选项。
    • 如果你有用于 Identity (_Layout.cshtml) 的现有自定义布局页面,请选择现有的布局页面,以避免基架使用不正确的标记覆盖布局。 例如,选择:
      • Razor Pages 的 Pages/Shared/_Layout.cshtml 或具有现有 Razor Pages 基础结构的 Blazor Server 项目。
      • MVC 项目的 Views/Shared/_Layout.cshtml 或具有现有 MVC 基础结构的 Blazor Server 项目。
    • 对于数据上下文(DbContext 类):
      • 选择数据上下文类。 必须至少选择一个文件才能添加数据上下文。
      • 若要创建数据上下文并可能为 Identity 创建新的用户类,请选择 + 按钮。 接受默认值或指定类(例如,对名为“Contoso”的公司使用 Contoso.Data.ApplicationDbContext)。 若要创建新的用户类,请选择用户类+ 按钮,并指定类(例如,对名为“Contoso”的公司使用 ContosoUser)。
    • 选择“添加”按钮以运行基架。

某些 Identity 选项是在 Areas/Identity/IdentityHostingStartup.cs 中配置的。 有关详细信息,请参阅 IHostingStartup

客户端 Blazor 应用

客户端 Blazor 应用使用自身的 Identity UI 方法,且不能使用 ASP.NET Core Identity 基架。 托管 Blazor 解决方案的服务器端 ASP.NET Core 应用可以遵循本文中的 Razor Pages/MVC 指南,其配置方式与支持 Identity 的任何其他类型的 ASP.NET Core 应用一样。

Blazor 框架不包括 Identity UI 页面的 Razor 组件版本。 Identity UI Razor 组件可以是自定义生成的,也可以从不受支持的第三方源获取。

有关详细信息,请参阅 Blazor 安全和 Identity 文章

创建完整的 Identity UI 源

若要维护 Identity UI 的完全控制,请运行 Identity 基架,并选择“替代所有文件”。

以下突出显示的代码显示了将默认 Identity UI 替换为 ASP.NET Core 2.1 Web 应用中的 Identity 的更改。 你可能希望执行此操作以对 Identity UI 具有完全控制。

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<IdentityUser, IdentityRole>()
        // services.AddDefaultIdentity<IdentityUser>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc()
        .AddRazorPagesOptions(options =>
        {
            options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
            options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
        });

    services.ConfigureApplicationCookie(options =>
    {
        options.LoginPath = $"/Identity/Account/Login";
        options.LogoutPath = $"/Identity/Account/Logout";
        options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
    });

    // using Microsoft.AspNetCore.Identity.UI.Services;
    services.AddSingleton<IEmailSender, EmailSender>();
}

在以下代码中替换默认 Identity:

services.AddIdentity<IdentityUser, IdentityRole>()
    // services.AddDefaultIdentity<IdentityUser>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

以下代码设置 LoginPathLogoutPathAccessDeniedPath

services.ConfigureApplicationCookie(options =>
{
    options.LoginPath = $"/Identity/Account/Login";
    options.LogoutPath = $"/Identity/Account/Logout";
    options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
});

注册 IEmailSender 实现,例如:

// using Microsoft.AspNetCore.Identity.UI.Services;
services.AddSingleton<IEmailSender, EmailSender>();
public class EmailSender : IEmailSender
{
    public Task SendEmailAsync(string email, string subject, string message)
    {
        return Task.CompletedTask;
    }
}

密码配置

如果 Startup.ConfigureServices 中配置了 PasswordOptions,则搭建的 Identity 页面中的 Password 属性可能需要配置 [StringLength] 特性InputModel Password 属性位于以下文件中:

  • Areas/Identity/Pages/Account/Register.cshtml.cs
  • Areas/Identity/Pages/Account/ResetPassword.cshtml.cs

禁用页面

本部分介绍如何禁用注册页面,而该方法可用于禁用任何页面。

禁用用户注册:

  • 搭建 Identity 的基架。 包括 Account.Register、Account.Login 和 Account.RegisterConfirmation。 例如:

    dotnet aspnet-codegenerator identity -dc RPauth.Data.ApplicationDbContext --files "Account.Register;Account.Login;Account.RegisterConfirmation"
    
  • 更新 Areas/Identity/Pages/Account/Register.cshtml.cs 以便用户无法从此终结点注册:

    public class RegisterModel : PageModel
    {
        public IActionResult OnGet()
        {
            return RedirectToPage("Login");
        }
    
        public IActionResult OnPost()
        {
            return RedirectToPage("Login");
        }
    }
    
  • 更新 Areas/Identity/Pages/Account/Register.cshtml 以便与前面的更改保持一致:

    @page
    @model RegisterModel
    @{
        ViewData["Title"] = "Go to Login";
    }
    
    <h1>@ViewData["Title"]</h1>
    
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
    
  • 注释禁止或从 Areas/Identity/Pages/Account/Login.cshtml 中删除注册链接

    @*
    <p>
        <a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
    </p>
    *@
    
  • 更新 Areas/Identity/Pages/Account/RegisterConfirmation 页。

    • 删除 cshtml 文件中的代码和链接。
    • 删除 PageModel 中的确认代码:
    [AllowAnonymous]
      public class RegisterConfirmationModel : PageModel
      {
          public IActionResult OnGet()
          {  
              return Page();
          }
      }
    

使用其他应用添加用户

提供一种在 Web 应用外部添加用户的机制。 用于添加用户的选项包括:

  • 专用的管理 Web 应用。
  • 控制台应用。

下面的代码概述了一种添加用户的方法:

  • 用户列表将读入内存中。
  • 为每个用户生成唯一强密码。
  • 用户已添加到 Identity 数据库。
  • 系统会通知用户并告知其更改密码。
public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                var context = services.GetRequiredService<AppDbCntx>();
                context.Database.Migrate();

                var config = host.Services.GetRequiredService<IConfiguration>();
                var userList = config.GetSection("userList").Get<List<string>>();

                SeedData.Initialize(services, userList).Wait();
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred adding users.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

下面的代码概述了如何添加用户:


public static async Task Initialize(IServiceProvider serviceProvider,
                                    List<string> userList)
{
    var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

    foreach (var userName in userList)
    {
        var userPassword = GenerateSecurePassword();
        var userId = await EnsureUser(userManager, userName, userPassword);

        NotifyUser(userName, userPassword);
    }
}

private static async Task<string> EnsureUser(UserManager<IdentityUser> userManager,
                                             string userName, string userPassword)
{
    var user = await userManager.FindByNameAsync(userName);

    if (user == null)
    {
        user = new IdentityUser(userName)
        {
            EmailConfirmed = true
        };
        await userManager.CreateAsync(user, userPassword);
    }

    return user.Id;
}

对于生产方案,可以遵循类似的方法。

禁止发布静态 Identity 资产

若要防止将静态 Identity 资产发布到 Web 根目录,请参阅 ASP.NET Core 上的 Identity 简介

其他资源

更改为 ASP.NET Core 2.1 及更高版本的身份验证代码