IdentityASP.NET Core 项目中的基架

作者:Rick Anderson

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

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

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

建议使用显示文件差异的源代码管理系统,并使您能够回退更改。 运行 scaffolder 后检查更改 Identity 。

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

当 Identity 使用现有个人帐户将具有新数据上下文的基架插入到项目中时:

  • 在中 Startup.ConfigureServices ,删除对的调用:
    • AddDbContext
    • AddDefaultIdentity

例如, AddDbContext AddDefaultIdentity 在以下代码中注释掉和:

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

前面的代码注释了 区域/ Identity / Identity HostingStartup 中的重复代码。

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

将基 Identity 架搭建到空项目中

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加新基架项 "对话框的 左窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
      • Blazor Server 默认情况下,不会为 pages 或 MVC () 模板 Blazor Server blazorserver Razor 创建的应用。 将布局页条目留空。
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
  • 选择 添加

使用 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 Core迁移 。 创建迁移并更新数据库。 例如,运行以下命令:

在 Visual Studio 程序包管理器 控制台中

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Add-Migration CreateIdentitySchema
Update-Database

命令的 Identity "创建架构"名称 Add-Migration 参数是任意的。 "CreateIdentitySchema" 描述迁移。

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

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加新基架项 "对话框的 左窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
      • Blazor Server 默认情况下,不会为 pages 或 MVC () 模板 Blazor Server blazorserver Razor 创建的应用。 将布局页条目留空。
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
  • 选择 添加

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

迁移、UseAuthentication 和布局

生成的数据库 Identity 代码需要Entity Framework Core迁移 。 创建迁移并更新数据库。 例如,运行以下命令:

在 Visual Studio 程序包管理器 控制台中

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Add-Migration CreateIdentitySchema
Update-Database

命令的 Identity "创建架构"名称 Add-Migration 参数是任意的。 "CreateIdentitySchema" 描述迁移。

启用身份验证

使用 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.min.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.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

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

在具有 Identity 授权的 Razor 项目中搭建基架

运行 Identity scaffolder:

  • 解决方案资源管理器 中,右键单击项目 > " 添加 > 新的基架项"。
  • 在 " 添加基架 " 对话框的左窗格中,选择 " Identity > 添加"。
  • 在 "添加 Identity " 对话框中,选择所需的选项。
    • 选择现有的布局页,以便不会用错误的标记覆盖布局文件。 如果选择了现有的 _ 布局 cshtml 文件,则 会覆盖它。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Blazor Server 包含现有 Razor 页面的基础结构的页面或项目
      • ~/Views/Shared/_Layout.cshtml 对于 Blazor Server 包含现有 mvc 基础结构的 mvc 项目或项目
  • 若要使用现有的数据上下文,请至少选择一个要重写的文件。 必须至少选择一个文件以添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 若要创建新的用户上下文,并可能创建的自定义用户类 Identity :
    • 选择 + 按钮以创建新的 数据上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则无需选择要重写的文件。

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加基架 "对话框的左 窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖。 选择现有 _ Layout.cshtml 文件时,不会 覆盖 该文件。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
  • 若要使用现有数据上下文,请至少选择要替代的一个文件。 必须至少选择一个文件来添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 创建新的用户上下文并可能为 创建自定义用户类 Identity :
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则不必选择要替代的文件。

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

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

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加新基架项 "对话框的 左窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
      • Blazor Server 默认情况下,不会为 pages 或 MVC () 模板 Blazor Server blazorserver Razor 创建的应用。 将布局页条目留空。
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
  • 选择 添加

可选:将登录部分 () _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.min.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.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.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 / Identity HostingStartup.cs 中配置。 有关详细信息,请参阅 IHostingStartup。

生成的数据库 Identity 代码需要Entity Framework Core迁移 。 创建迁移并更新数据库。 例如,运行以下命令:

在 Visual Studio 程序包管理器 控制台中

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Add-Migration CreateIdentitySchema
Update-Database

命令的 Identity "创建架构"名称 Add-Migration 参数是任意的。 "CreateIdentitySchema" 描述迁移。

使用 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 scaffolder:

  • 解决方案资源管理器 中,右键单击项目 > " 添加 > 新的基架项"。
  • 在 " 添加基架 " 对话框的左窗格中,选择 " Identity > 添加"。
  • 在 "添加 Identity " 对话框中,选择所需的选项。
    • 选择现有的布局页,以便不会用错误的标记覆盖布局文件。 如果选择了现有的 _ 布局 cshtml 文件,则 会覆盖它。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Blazor Server 包含现有 Razor 页面的基础结构的页面或项目
      • ~/Views/Shared/_Layout.cshtml 对于 Blazor Server 包含现有 mvc 基础结构的 mvc 项目或项目
  • 若要使用现有的数据上下文,请至少选择一个要重写的文件。 必须至少选择一个文件以添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 若要创建新的用户上下文,并可能创建的自定义用户类 Identity :
    • 选择 + 按钮以创建新的 数据上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则无需选择要重写的文件。

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加基架 "对话框的左 窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖。 选择现有 _ Layout.cshtml 文件时,不会 覆盖 该文件。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
  • 若要使用现有数据上下文,请至少选择要替代的一个文件。 必须至少选择一个文件来添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 创建新的用户上下文并可能为 创建自定义用户类 Identity :
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则不必选择要替代的文件。

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

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加新基架项 "对话框的 左窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
      • Blazor Server 默认情况下,不会为 pages 或 MVC () 模板 Blazor Server blazorserver Razor 创建的应用。 将布局页条目留空。
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
  • 选择 添加

Identity在 区域/ Identity / Identity HostingStartup 中配置。 有关详细信息,请参阅 IHostingStartup

迁移

生成的数据库 Identity 代码需要Entity Framework Core迁移 。 创建迁移并更新数据库。 例如,运行以下命令:

在 Visual Studio 程序包管理器 控制台中

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Add-Migration CreateIdentitySchema
Update-Database

命令的 Identity "创建架构"名称 Add-Migration 参数是任意的。 "CreateIdentitySchema" 描述迁移。

将 XSRF 令牌传递给应用

可以将令牌传递给组件:

  • 设置身份验证令牌并将其保存到身份验证后 cookie ,可以将其传递给组件。
  • Razor 组件不能 HttpContext 直接使用,因此无法获取 (XSRF 的) 令牌的反请求伪造 Identity /Identity/Account/Logout 。 可以将 XSRF 令牌传递给组件。

有关详细信息,请参阅 ASP.NET Core Blazor Server 其他安全方案

Pages/_Host cshtml 文件中,在将其添加到和类后建立该令牌 InitialApplicationState TokenProvider

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf

...

var tokens = new InitialApplicationState
{
    ...

    XsrfToken = Xsrf.GetAndStoreTokens(HttpContext).RequestToken
};

App将组件更新 () ,以分配 InitialState.XsrfToken

@inject TokenProvider TokenProvider

...

TokenProvider.XsrfToken = InitialState.XsrfToken;

TokenProvider 主题中演示的服务在 LoginDisplay 以下 布局和身份验证流更改 部分的组件中使用。

启用身份验证

Startup 类中:

  • 确认 Razor 在中添加了页面服务 Startup.ConfigureServices
  • 如果使用 TokenProvider,请注册服务。
  • UseDatabaseErrorPage对于开发环境,请在中的应用程序生成器上调用 Startup.Configure
  • 调用 UseAuthentication and UseAuthorization after UseRouting
  • 添加页的终结点 Razor 。
public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton<WeatherForecastService>();
    services.AddScoped<TokenProvider>();
}

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();
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
    });
}

UseHsts 建议,但不是必需的。 有关详细信息,请参阅 HTTP 严格传输安全协议

布局和身份验证流更改

RedirectToLogin RedirectToLogin) 的 (组件添加到项目根目录中应用的 共享 文件夹中:

@inject NavigationManager Navigation
@code {
    protected override void OnInitialized()
    {
        Navigation.NavigateTo("Identity/Account/Login?returnUrl=" +
            Uri.EscapeDataString(Navigation.Uri), true);
    }
}

LoginDisplay () 的组件添加到应用的 共享 文件夹。 TokenProvider 服务提供 HTML 窗体的 XSRF 标记,该标记将发布到 Identity 注销终结点:

@using Microsoft.AspNetCore.Components.Authorization
@inject NavigationManager Navigation
@inject TokenProvider TokenProvider

<AuthorizeView>
    <Authorized>
        <a href="Identity/Account/Manage/Index">
            Hello, @context.User.Identity.Name!
        </a>
        <form action="/Identity/Account/Logout?returnUrl=%2F" method="post">
            <button class="nav-link btn btn-link" type="submit">Logout</button>
            <input name="__RequestVerificationToken" type="hidden" 
                value="@TokenProvider.XsrfToken">
        </form>
    </Authorized>
    <NotAuthorized>
        <a href="Identity/Account/Register">Register</a>
        <a href="Identity/Account/Login">Login</a>
    </NotAuthorized>
</AuthorizeView>

MainLayout Shared/MainLayout.razor (组件) ,将组件添加到 LoginDisplay 首行 <div> 元素的内容:

<div class="top-row px-4 auth">
    <LoginDisplay />
    <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>

样式身份验证终结点

因为 使用 Pages 页面,所以当访问者在页面和组件之间导航时,UI 的样式 Blazor Server Razor Identity Identity 会发生变化。 有两个选项可以解决不协调的样式:

生成 Identity 组件

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

将自定义布局与 Blazor 应用样式一同使用

可以 Identity 修改页面布局和样式,以生成使用默认主题 Blazor 的页面。

备注

本部分中的示例只是自定义的起点。 为获得最佳用户体验,可能需要执行额外的工作。

使用 NavMenu_IdentityLayout Shared/ (Identity Layout.razor NavMenu_ 创建新的组件) 。 对于组件的标记和代码,请使用 NavMenu Shared/NavMenu.razor (应用组件的相同) 。 从无法匿名访问的组件中去除任何 ,因为组件中的自动重定向对于需要身份验证或授权 NavLink RedirectToLogin 的组件失败。

Pages/Shared/Layout.cshtml 文件中,进行以下更改:

  • 将 Razor 指令添加到文件顶部,以使用标记帮助程序以及 Shared 文件夹中 的应用 组件:

    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    @using {APPLICATION ASSEMBLY}.Shared
    

    {APPLICATION ASSEMBLY} 替换为应用的程序集名称。

  • <base> 内容添加 Blazor 标记 <link> 和样式 <head> 表:

    <base href="~/" />
    <link rel="stylesheet" href="~/css/site.css" />
    
  • 将 标记 <body> 的内容更改为以下内容:

    <div class="sidebar" style="float:left">
        <component type="typeof(NavMenu_IdentityLayout)" 
            render-mode="ServerPrerendered" />
    </div>
    
    <div class="main" style="padding-left:250px">
        <div class="top-row px-4">
            @{
                var result = Engine.FindView(ViewContext, "_LoginPartial", 
                    isMainPage: false);
            }
            @if (result.Success)
            {
                await Html.RenderPartialAsync("_LoginPartial");
            }
            else
            {
                throw new InvalidOperationException("The default Identity UI " +
                    "layout requires a partial view '_LoginPartial'.");
            }
            <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
        </div>
    
        <div class="content px-4">
            @RenderBody()
        </div>
    </div>
    
    <script src="~/Identity/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/Identity/js/site.js" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)
    <script src="_framework/blazor.server.js"></script>
    

在具有 Identity 授权的 Blazor Server 项目中搭建基架

运行 Identity scaffolder:

  • 解决方案资源管理器 中,右键单击项目 > " 添加 > 新的基架项"。
  • 在 " 添加基架 " 对话框的左窗格中,选择 " Identity > 添加"。
  • 在 "添加 Identity " 对话框中,选择所需的选项。
    • 选择现有的布局页,以便不会用错误的标记覆盖布局文件。 如果选择了现有的 _ 布局 cshtml 文件,则 会覆盖它。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Blazor Server 包含现有 Razor 页面的基础结构的页面或项目
      • ~/Views/Shared/_Layout.cshtml 对于 Blazor Server 包含现有 mvc 基础结构的 mvc 项目或项目
  • 若要使用现有的数据上下文,请至少选择一个要重写的文件。 必须至少选择一个文件以添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 若要创建新的用户上下文,并可能创建的自定义用户类 Identity :
    • 选择 + 按钮以创建新的 数据上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则无需选择要重写的文件。

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加基架 "对话框的左 窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖。 选择现有 _ Layout.cshtml 文件时,不会 覆盖 该文件。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
  • 若要使用现有数据上下文,请至少选择要替代的一个文件。 必须至少选择一个文件来添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 创建新的用户上下文并可能为 创建自定义用户类 Identity :
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则不必选择要替代的文件。

某些 Identity 选项在 区域/ Identity / Identity HostingStartup 中配置。 有关详细信息,请参阅 IHostingStartup

独立应用或托管 Blazor WebAssembly 应用

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

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

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

创建完整的 Identity UI 源

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

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

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().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
        .AddRazorPagesOptions(options =>
        {
            options.AllowAreas = true;
            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;
    }
}

密码配置

如果 PasswordOptions 在中配置了 Startup.ConfigureServices ,则基架页中的属性可能需要 [StringLength] 属性配置 Password Identity 。 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"
    
  • 更新 区域 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>
    *@
    
  • 更新 "区域 Identity //页面/帐户/注册""确认" 页。

    • 从 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 和更高版本提供 ASP.NET Core Identity Razor 类库。 包含的应用程序 Identity 可以应用 scaffolder 将类库中包含的源代码选择性地添加 Identity Razor (RCL) 。 建议生成源代码,以便修改代码和更改行为。 例如,可以指示基架生成在注册过程中使用的代码。 生成的代码优先于 Identity RCL 中的相同代码。 若要完全控制 UI,而不使用默认的 RCL,请参阅 创建完全标识 UI 源部分。

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

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

当 Identity scaffolder 运行时,将在项目目录中创建一个 ScaffoldingReadme.txt 文件。 ScaffoldingReadme.txt 文件包含有关完成基架更新所需内容的一般说明 Identity 。 本文档包含与 ScaffoldingReadme.txt 文件更完整的说明。

建议使用显示文件差异的源代码管理系统,并使您能够回退更改。 运行 scaffolder 后检查更改 Identity 。

备注

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

基架 Identity 到空项目

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加新基架项 "对话框的 左窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
      • Blazor Server 默认情况下,不会为 pages 或 MVC () 模板 Blazor Server blazorserver Razor 创建的应用。 将布局页条目留空。
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
  • 选择 添加

将以下突出显示的调用添加到 Startup 类:

public class Startup
{        
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseMvc();
    }
}

UseHsts 建议,但不是必需的。 有关详细信息,请参阅 HTTP 严格传输安全协议

生成的数据库 Identity 代码需要Entity Framework Core迁移 。 创建迁移并更新数据库。 例如,运行以下命令:

在 Visual Studio 程序包管理器 控制台中

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Add-Migration CreateIdentitySchema
Update-Database

命令的 Identity "创建架构"名称 Add-Migration 参数是任意的。 "CreateIdentitySchema" 描述迁移。

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

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加新基架项 "对话框的 左窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
      • Blazor Server 默认情况下,不会为 pages 或 MVC () 模板 Blazor Server blazorserver Razor 创建的应用。 将布局页条目留空。
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
  • 选择 添加

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

迁移、UseAuthentication 和布局

生成的数据库 Identity 代码需要Entity Framework Core迁移 。 创建迁移并更新数据库。 例如,运行以下命令:

在 Visual Studio 程序包管理器 控制台中

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Add-Migration CreateIdentitySchema
Update-Database

命令的 Identity "创建架构"名称 Add-Migration 参数是任意的。 "CreateIdentitySchema" 描述迁移。

启用身份验证

Configure 类的 方法 Startup 中,在 UseAuthentication 之后调用 UseStaticFiles

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

    public IConfiguration Configuration { get; }

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

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

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

        app.UseMvc();
    }
}

UseHsts 建议,但不是必需的。 有关详细信息,请参阅 HTTP 严格传输安全协议

布局更改

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

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorNoAuth8</title>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    </environment>
</head>
<body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a asp-page="/Index" class="navbar-brand">RazorNoAuth8</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-page="/Index">Home</a></li>
                    <li><a asp-page="/About">About</a></li>
                    <li><a asp-page="/Contact">Contact</a></li>
                </ul>
                <partial name="_LoginPartial" />
            </div>
        </div>
    </nav>

    <partial name="_CookieConsentPartial" />

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2018 - RazorNoAuth8</p>
        </footer>
    </div>

    <environment include="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment exclude="Development">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>

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

在具有 Identity 授权的 Razor 项目中搭建基架

运行 Identity scaffolder:

  • 解决方案资源管理器 中,右键单击项目 > " 添加 > 新的基架项"。
  • 在 " 添加基架 " 对话框的左窗格中,选择 " Identity > 添加"。
  • 在 "添加 Identity " 对话框中,选择所需的选项。
    • 选择现有的布局页,以便不会用错误的标记覆盖布局文件。 如果选择了现有的 _ 布局 cshtml 文件,则 会覆盖它。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Blazor Server 包含现有 Razor 页面的基础结构的页面或项目
      • ~/Views/Shared/_Layout.cshtml 对于 Blazor Server 包含现有 mvc 基础结构的 mvc 项目或项目
  • 若要使用现有的数据上下文,请至少选择一个要重写的文件。 必须至少选择一个文件以添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 若要创建新的用户上下文,并可能创建的自定义用户类 Identity :
    • 选择 + 按钮以创建新的 数据上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则无需选择要重写的文件。

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加基架 "对话框的左 窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖。 选择现有 _ Layout.cshtml 文件时,不会 覆盖 该文件。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
  • 若要使用现有数据上下文,请至少选择要替代的一个文件。 必须至少选择一个文件来添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 创建新的用户上下文并可能为 创建自定义用户类 Identity :
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则不必选择要替代的文件。

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

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

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加新基架项 "对话框的 左窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
      • Blazor Server 默认情况下,不会为 pages 或 MVC () 模板 Blazor Server blazorserver Razor 创建的应用。 将布局页条目留空。
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
  • 选择 添加

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

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - MvcNoAuth3</title>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    </environment>
</head>
<body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">MvcNoAuth3</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
                    <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
                    <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
                </ul>
                <partial name="_LoginPartial" />
            </div>
        </div>
    </nav>

    <partial name="_CookieConsentPartial" />

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2018 - MvcNoAuth3</p>
        </footer>
    </div>

    <environment include="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment exclude="Development">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>

    @RenderSection("Scripts", required: false)
</body>
</html>
  • Pages/Shared/_LoginPartial.cshtml 文件移到 Views/Shared/_LoginPartial.cshtml

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

生成的数据库 Identity 代码需要Entity Framework Core迁移 。 创建迁移并更新数据库。 例如,运行以下命令:

在 Visual Studio 程序包管理器 控制台中

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Add-Migration CreateIdentitySchema
Update-Database

命令的 Identity "创建架构"名称 Add-Migration 参数是任意的。 "CreateIdentitySchema" 描述迁移。

UseAuthentication 之后调用 UseStaticFiles

public class Startup
{

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

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

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseMvcWithDefaultRoute();
    }
}

UseHsts 建议,但不是必需的。 有关详细信息,请参阅 HTTP 严格传输安全协议

在具有 Identity 授权的 MVC 项目中搭建基架

运行 Identity scaffolder:

  • 解决方案资源管理器 中,右键单击项目 > " 添加 > 新的基架项"。
  • 在 " 添加基架 " 对话框的左窗格中,选择 " Identity > 添加"。
  • 在 "添加 Identity " 对话框中,选择所需的选项。
    • 选择现有的布局页,以便不会用错误的标记覆盖布局文件。 如果选择了现有的 _ 布局 cshtml 文件,则 会覆盖它。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Blazor Server 包含现有 Razor 页面的基础结构的页面或项目
      • ~/Views/Shared/_Layout.cshtml 对于 Blazor Server 包含现有 mvc 基础结构的 mvc 项目或项目
  • 若要使用现有的数据上下文,请至少选择一个要重写的文件。 必须至少选择一个文件以添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 若要创建新的用户上下文,并可能创建的自定义用户类 Identity :
    • 选择 + 按钮以创建新的 数据上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则无需选择要重写的文件。

运行 Identity 基架:

  • 解决方案资源管理器" 中,右键单击项目 >" > 添加新基架项"。
  • 在"添加基架 "对话框的左 窗格中,选择"添加 Identity > "。
  • 在"添加 Identity"对话框中,选择想要的选项。
    • 选择现有的布局页,否则布局文件将被不正确的标记覆盖。 选择现有 _ Layout.cshtml 文件时,不会 覆盖 该文件。 例如:
      • ~/Pages/Shared/_Layout.cshtml 对于 Razor Pages
      • ~/Views/Shared/_Layout.cshtml 对于 MVC 项目
  • 若要使用现有数据上下文,请至少选择要替代的一个文件。 必须至少选择一个文件来添加数据上下文。
    • 选择数据上下文类。
    • 选择 添加
  • 创建新的用户上下文并可能为 创建自定义用户类 Identity :
    • 选择 + 按钮以创建新的数据 上下文类。 接受默认值或指定类 (例如 MyApplication.Data.ApplicationDbContext ,) 。
    • 选择 添加

注意:如果要创建新的用户上下文,则不必选择要替代的文件。

删除 Pages/Shared 文件夹以及该文件夹中的文件。

创建完整的 Identity UI 源

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

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

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().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
        .AddRazorPagesOptions(options =>
        {
            options.AllowAreas = true;
            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;
    }
}

密码配置

如果 PasswordOptions 在中配置了 Startup.ConfigureServices ,则基架页中的属性可能需要 [StringLength] 属性配置 Password Identity 。 InputModel``Password在以下文件中可以找到属性:

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

禁用注册页

禁用用户注册:

  • 基架 Identity 。 包括帐户. Register、RegisterConfirmation。 例如:

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

    public class RegisterModel : PageModel
    {
        public IActionResult OnGet()
        {
            return RedirectToPage("Login");
        }
    
        public IActionResult OnPost()
        {
            return RedirectToPage("Login");
        }
    }
    
  • 更新 区域/ 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>
    
  • 注释掉或删除 区域/ Identity /Pages/Account/Login.cshtml 中的注册链接

@*
<p>
    <a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
</p>
*@
  • 更新 " 区域/ 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;
}

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

其他资源