项目 Identity ASP.NET Core基架

作者:Rick Anderson

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

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

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

建议使用显示文件差异并允许你退出更改的源代码管理系统。 运行基架后 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();
}

前面的代码注释掉 Areas/ Identity / Identity HostingStartup.cs 中重复的代码

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

将基 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在 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" 描述迁移。

将 XSRF 令牌传递给应用

可以将令牌传递给组件:

  • 预配身份验证令牌并保存到身份验证时 cookie ,可以将这些令牌传递给组件。
  • Razor组件不能直接使用,因此无法获取反请求伪造, (XSRF) 令牌 POST 到 的 HttpContext 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 App.razor (组件) 以分配 InitialState.XsrfToken

@inject TokenProvider TokenProvider

...

TokenProvider.XsrfToken = InitialState.XsrfToken;

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

启用身份验证

Startup 类中:

  • 确认 Razor Pages 服务已添加到 Startup.ConfigureServices 中。
  • 如果使用 TokenProvider,请注册服务。
  • UseDatabaseErrorPage 中的应用程序生成器上为 Startup.Configure 开发环境调用 。
  • UseAuthentication 之后 UseAuthorization 调用 和 UseRouting
  • 为 Pages 添加 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.razor) 添加到项目根中的应用的 Shared 文件夹:

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

LoginDisplay LoginDisplay.razor (添加到) 的 Shared 文件夹中。 TokenProvider 服务为 POST 到 的注销终结点的 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 选项在 Areas/ Identity / Identity HostingStartup.cs 中配置。 有关详细信息,请参阅 IHostingStartup

独立或托管 Blazor WebAssembly 应用

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

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

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

创建完整的 Identity UI 源

若要保持对 UI 的 Identity 完全控制,请运行 Identity 基架并选择"重写所有文件"。

以下突出显示的代码显示了在 Identity 2.1 Web 应用中将默认 UI 替换为 ASP.NET Core 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().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();

以下代码设置LoginPath、LogoutPath AccessDeniedPath:

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 Identity Razor RCL (中) 。 建议生成源代码,以便修改代码和更改行为。 例如,可以指示基架生成在注册过程中使用的代码。 生成的代码优先于 Identity RCL 中的相同代码。 若要获取 UI 的完全控制权,而不是使用默认 RCL,请参阅创建 完整标识 UI 源部分

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

虽然基架会生成大部分必需的代码,但必须更新项目才能完成该过程。 本文档介绍完成基架 Identity 更新所需的步骤。

运行 Identity 基架时,ScaffoldingReadme.txt目录中创建一个扩展文件。 此 ScaffoldingReadme.txt 文件包含有关完成基架更新所需的 Identity 一般说明。 本文档包含比文档文件 ScaffoldingReadme.txt说明。

建议使用显示文件差异并允许你退出更改的源代码管理系统。 运行基架后 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 基架并选择"重写所有文件"。

以下突出显示的代码显示了在 Identity 2.1 Web 应用中将默认 UI 替换为 ASP.NET Core 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().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();

以下代码设置LoginPath、LogoutPath AccessDeniedPath:

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

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

其他资源