.NET 10 中的 ASP.NET Core Identity — 从“登录页面”到生产级安全

目录

[1. 心智模型:ASP.NET Core Identity 的真正含义](#1. 心智模型:ASP.NET Core Identity 的真正含义)

[2. .NET 10 项目设置(精简但实用)](#2. .NET 10 项目设置(精简但实用))

Program.cs

应用程序数据库上下文

[3. 身份构建模块:用户、角色、声明、令牌](#3. 身份构建模块:用户、角色、声明、令牌)

[3.1 用户管理器和登录管理器](#3.1 用户管理器和登录管理器)

[3.2 角色、索赔与保单](#3.2 角色、索赔与保单)

[4. 像安全工程师一样配置身份](#4. 像安全工程师一样配置身份)

您应该了解的安全关键领域

[5. 身份验证 Cookie、方案和身份验证管道](#5. 身份验证 Cookie、方案和身份验证管道)

[5.1 Cookie 方案结构](#5.1 Cookie 方案结构)

[6. 授权:从角色到基于策略的访问控制](#6. 授权:从角色到基于策略的访问控制)

[6.1 注册政策](#6.1 注册政策)

[6.2 自定义需求及处理程序](#6.2 自定义需求及处理程序)

[7. 使用最小 API 和 SPA 实现身份验证](#7. 使用最小 API 和 SPA 实现身份验证)

[7.1 使用身份 cookie 保护最小 API](#7.1 使用身份 cookie 保护最小 API)

[7.2 JWT 持有者 + 身份](#7.2 JWT 持有者 + 身份)

[8. 无痛扩展身份模型](#8. 无痛扩展身份模型)

[8.1 扩展用户](#8.1 扩展用户)

[8.2 查询扩展用户](#8.2 查询扩展用户)

[9. 生产环境相关问题:密码重置、双因素身份验证、电子邮件验证、账户锁定](#9. 生产环境相关问题:密码重置、双因素身份验证、电子邮件验证、账户锁定)

[9.1 密码重置流程(概要)](#9.1 密码重置流程(概要))

[9.2 双因素认证 (2FA)](#9.2 双因素认证 (2FA))

[9.3 锁定和暴力破解保护](#9.3 锁定和暴力破解保护)

[9.4 电子邮件发送和后台工作](#9.4 电子邮件发送和后台工作)

[10. 检查清单:在 .NET 10 中强化 ASP.NET Core Identity](#10. 检查清单:在 .NET 10 中强化 ASP.NET Core Identity)

[10.1 基础知识](#10.1 基础知识)

[10.2 账户生命周期](#10.2 账户生命周期)

[10.3 授权设计](#10.3 授权设计)

[10.4 多环境、多租户](#10.4 多环境、多租户)

[10.5 可观测性和运行](#10.5 可观测性和运行)

最后想说的话


如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

大多数 .NET 开发人员通过 aa 模板接触 ASP.NET Core Identity:

"按下F5键,点击**注册** ,它就会神奇地在数据库中创建用户。"

这是一个不错的演示。

但在实际产品中,你会很快遇到更棘手的问题:

  • 如何加强生产环境中的身份安全(cookie、锁定、密码策略、双因素身份验证)?
  • 如何将身份验证与最少的 API 和 SPA 前端集成?
  • 角色、声明和策略在底层是如何运作的
  • 如何在不破坏迁移或安全性的前提下扩展模式?
  • 如何为多租户、外部身份提供商和零信任后端做好准备?

本文是一篇面向 .NET 10 的指南,它超越了简单的框架搭建:我们将采用熟悉的 Identity 堆栈,并将其塑造成您可以在生产环境中放心运行的东西。

我们假设您的目标平台是**.NET 10**(但除非另有说明,否则此处的所有内容都适用于 .NET 8+)。

1. 心智模型:ASP.NET Core Identity 的真正含义

身份认证不仅仅是"登录页面"。可以把它想象成三个层次:

  1. 持久层(Entity Framework Core)

    类似这样 的表格AspNetUsers、``AspNetRoles、``AspNetUserClaims、``AspNetUserLogins...``UserManager<TUser>RoleManager<TRole>协调读写操作。

  2. 身份验证层(Cookie/令牌)

    登录成功后颁发身份验证 cookie(或持有者令牌)。
    使用 ASP.NET Core 身份验证处理程序(cookie、JWT bearer 等)。

  3. 授权层(角色、声明、策略)

    应用[Authorize]和策略来控制认证用户的操作。

使用此模板时,您将获得所有三个选项,并采用保守的默认值。作为高级开发人员,您:

  • 决定开启/关闭哪些功能(双因素身份验证、锁定、外部提供商)。
  • 构建用户模型。
  • 将身份验证与您的应用程序架构(MVC、Razor Pages、最小 API、SPA)集成。

2. .NET 10 项目设置(精简但实用)

让我们从一个使用 Identity 和 EF Core 的最小 ASP.NET Core 应用程序开始。

dotnet new webapp -n IdentityNet10Demo

cd IdentityNet10Demo

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore

dotnet add package Microsoft.EntityFrameworkCore.SqlServer

dotnet add package Microsoft.EntityFrameworkCore.Tools

Program.cs

using Microsoft.AspNetCore.Identity;

using Microsoft.EntityFrameworkCore;

using IdentityNet10Demo.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")

?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");

builder.Services.AddDbContext<ApplicationDbContext>(options =>

options.UseSqlServer(connectionString));

builder.Services

.AddDefaultIdentity<IdentityUser>(options =>

{

options.SignIn.RequireConfirmedAccount = true;

})

.AddRoles<IdentityRole>()

.AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())

{

app.UseExceptionHandler("/Error");

app.UseHsts();

}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

应用程序数据库上下文

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;

using Microsoft.EntityFrameworkCore;

namespace IdentityNet10Demo.Data;

public sealed class ApplicationDbContext : IdentityDbContext

{

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)

: base(options)

{

}

}

在appsettings.json 文件中添加连接字符串,然后运行:

dotnet ef migrations add InitialIdentitySchema

dotnet ef database update

现在您已经拥有一个可以正常运行的 .NET 应用程序,其中包含身份表和基本的登录/注册流程。接下来,我们将把它升级为生产级设计。

3. 身份构建模块:用户、角色、声明、令牌

运行时身份识别主要围绕几个核心类型展开:

3.1 用户管理器和登录管理器

public class AccountController : Controller

{

private readonly UserManager<IdentityUser> _userManager;

private readonly SignInManager<IdentityUser> _signInManager;

public AccountController(

UserManager<IdentityUser> userManager,

SignInManager<IdentityUser> signInManager)

{

_userManager = userManager;

_signInManager = signInManager;

}

public async Task<IActionResult> Login(LoginViewModel model)

{

if (!ModelState.IsValid)

return View(model);

var result = await _signInManager.PasswordSignInAsync(

model.Email,

model.Password,

isPersistent: model.RememberMe,

lockoutOnFailure: true);

if (result.Succeeded)

return RedirectToAction("Index", "Home");

if (result.IsLockedOut)

return View("Lockout");

ModelState.AddModelError(string.Empty, "Invalid login attempt.");

return View(model);

}

}

  • UserManager<TUser>处理用户生命周期:创建、更新、密码哈希、电子邮件确认、双因素身份验证、令牌。
  • SignInManager<TUser>处理登录工作流程:cookie 发放、锁定、双因素身份验证、外部提供商。

3.2 角色、索赔与保单

  • 角色 :粗粒度标签(,,Admin)。 Moderator``Customer
  • 声明 :随用户传递的键/值对("department" = "Finance""subscription" = "Pro")。
  • 策略 :检查角色和/或声明的规则(例如"用户必须拥有department = FinanceIsManager = true")。

在 .NET 10 风格的应用程序中,通常会将角色保持在最低限度,并通过声明 + 策略进行最细粒度的授权。

4. 像安全工程师一样配置身份

模板会选择安全的默认值,但生产系统需要进行显式配置

builder.Services.AddDefaultIdentity<ApplicationUser>(options =>

{

// Password

options.Password.RequiredLength = 12;

options.Password.RequireNonAlphanumeric = true;

options.Password.RequireUppercase = true;

options.Password.RequireLowercase = true;

options.Password.RequireDigit = true;

// Lockout

options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);

options.Lockout.MaxFailedAccessAttempts = 5;

options.Lockout.AllowedForNewUsers = true;

// User

options.User.RequireUniqueEmail = true;

// Sign‑in

options.SignIn.RequireConfirmedAccount = true;

options.SignIn.RequireConfirmedEmail = true;

})

.AddRoles<IdentityRole>()

.AddEntityFrameworkStores<ApplicationDbContext>();

您应该了解的安全关键领域

身份验证的默认设置IdentityUser包含一些容易被忽略但非常重要的字段:

  • SecurityStamp-- 当身份验证 cookie 发生更改时(密码重置、双因素身份验证更改等),该 cookie 会失效。
  • ConcurrencyStamp-- 防止数据库级别的更新丢失。
  • LockoutEndAccessFailedCount-- 驱动锁定逻辑。
  • TwoFactorEnabled-- 控制登录时是否需要双因素身份验证。

扩展 Identity 时,不要删除这些;而是使用继承进行扩展。

public sealed class ApplicationUser : IdentityUser

{

public string FullName { get; set; } = default!;

public DateTime RegisteredAtUtc { get; init; } = DateTime.UtcNow;

// Example of a simple multi‑tenant flag

public string TenantId { get; set; } = default!;

}

请记得更新 DbContext 泛型:

public sealed class ApplicationDbContext

: IdentityDbContext<ApplicationUser, IdentityRole, string>

{

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)

: base(options) { }

}

5. 身份验证 Cookie、方案和身份验证管道

Identity底层使用标准的ASP.NET Core身份验证系统。

典型的 .NET 10 应用程序将依赖 cookie 身份验证来实现服务器端渲染的 UI,并可能依赖 JWT 持有者来实现 API。

Identity 注册了一个名为(默认名称)的 cookie 方案Identity.Application。该 cookie:

  • 包含用户主体(名称、角色、声明)。
  • 使用 ASP.NET Core 数据保护密钥 进行加密和签名。
  • 具有有效期/滑动有效期配置。

您可以进行自定义设置:

builder.Services.ConfigureApplicationCookie(options =>

{

options.Cookie.Name = ".IdentityNet10.Auth";

options.Cookie.HttpOnly = true;

options.Cookie.SecurePolicy = CookieSecurePolicy.Always;

options.Cookie.SameSite = SameSiteMode.Lax; // or Strict for pure server‑rendered sites

options.LoginPath = "/Account/Login";

options.LogoutPath = "/Account/Logout";

options.AccessDeniedPath = "/Account/AccessDenied";

options.SlidingExpiration = true;

options.ExpireTimeSpan = TimeSpan.FromHours(8);

});

对于具有独立前端的单页应用程序 (SPA),通常会将以下功能结合起来:

  • 用于访问 Razor Pages / BFF 的 人类用户的 Cookie 身份验证。
  • API 调用的访问令牌(AddJwtBearer)。

身份验证并不关心你使用哪个处理程序,只要你正确配置了方案和登录逻辑即可。

6. 授权:从角色到基于策略的访问控制

让我们从以下内容开始:

Authorize(Roles = "Admin")

......转向基于策略的授权,这种方式更加灵活,也更容易发展。

6.1 注册政策

builder.Services.AddAuthorization(options =>

{

options.AddPolicy("RequireAdmin", policy =>

policy.RequireRole("Admin"));

options.AddPolicy("PaidUser", policy =>

policy.RequireClaim("subscription", "Pro", "Business"));

options.AddPolicy("FinanceManager", policy =>

policy.RequireAssertion(context =>

context.User.HasClaim("department", "Finance") &&

context.User.HasClaim("is_manager", "true")));

});

在控制器、Razor Pages 或极简 API 中使用它们:

Authorize(Policy = "FinanceManager")

public IActionResult FinancialDashboard() => View();

6.2 自定义需求及处理程序

对于更复杂的规则(例如,用户必须拥有该资源),请构建自定义要求:

public sealed class SameTenantRequirement : IAuthorizationRequirement { }

public sealed class SameTenantHandler : AuthorizationHandler<SameTenantRequirement>

{

protected override Task HandleRequirementAsync(

AuthorizationHandlerContext context,

SameTenantRequirement requirement)

{

var userTenant = context.User.FindFirst("tenant_id")?.Value;

var resourceTenant = (context.Resource as ITenantScopedResource)?.TenantId;

if (userTenant is not null &&

resourceTenant is not null &&

userTenant == resourceTenant)

{

context.Succeed(requirement);

}

return Task.CompletedTask;

}

}

登记:

builder.Services.AddSingleton<IAuthorizationHandler, SameTenantHandler>();

builder.Services.AddAuthorization(options =>

{

options.AddPolicy("SameTenantOnly", policy =>

policy.AddRequirements(new SameTenantRequirement()));

});

现在,您的控制器/最小 API 可以使用单个属性来强制执行多租户边界。

7. 使用最小 API 和 SPA 实现身份验证

在 .NET 8+(以及 .NET 10)中,许多团队使用 React/Angular/Blazor 或移动客户端构建最小的 API 后端。

var api = app.MapGroup("/api")

.RequireAuthorization(); // default policy

api.MapGet("/me", async (UserManager<ApplicationUser> users, ClaimsPrincipal user) =>

{

var appUser = await users.GetUserAsync(user);

return appUser is null

? Results.Unauthorized()

: Results.Ok(new

{

appUser.Email,

appUser.FullName,

appUser.RegisteredAtUtc

});

});

如果前端与目标服务器共享相同的来源和 cookie,那么这种方法就能正常工作。

7.2 JWT 持有者 + 身份

如果您更喜欢代币:

  1. 使用身份管理进行用户管理。
  2. 使用自定义端点颁发 JWT JwtSecurityTokenHandler
  3. 使用以下方式保护 API AddAuthentication().AddJwtBearer(...)

身份认证并不强制使用 cookie;它提供了一个用户存储和安全原语,您可以根据需要将其公开。

8. 无痛扩展身份模型

迟早你需要为用户添加额外的字段:订阅级别、偏好设置、租户等。

8.1 扩展用户

我们已经看到了ApplicationUser : IdentityUser。请在此处添加您的字段并重新运行迁移:

dotnet ef migrations add AddTenantToApplicationUser

dotnet ef database update

避免:

在迁移之外手动编辑默认的身份表。

使用单独的"个人资料"表来存放几乎总是需要的基本字段,通常会使查询变得复杂。

8.2 查询扩展用户

public sealed class UsersController : Controller

{

private readonly UserManager<ApplicationUser> _userManager;

public UsersController(UserManager<ApplicationUser> userManager)

{

_userManager = userManager;

}

Authorize(Policy = "RequireAdmin")

public async Task<IActionResult> Index()

{

var users = await _userManager.Users

.OrderBy(u => u.Email)

.Select(u => new UserListItemDto(

u.Id,

u.Email!,

u.FullName,

u.TenantId,

u.RegisteredAtUtc))

.ToListAsync();

return View(users);

}

}

public sealed record UserListItemDto(

string Id,

string Email,

string FullName,

string TenantId,

DateTime RegisteredAtUtc);

因为标识表只是 EF Core 实体,所以您可以像对待其他任何数据一样将它们投影到 DTO。

9. 生产环境相关问题:密码重置、双因素身份验证、电子邮件验证、账户锁定

Identity 拥有丰富的令牌系统,可用于密码重置、电子邮件确认、更改电子邮件等。

9.1 密码重置流程(概要)

  1. 用户在"忘记密码"页面提交电子邮件地址。
  2. 您生成一个令牌:

var user = await _userManager.FindByEmailAsync(model.Email);

if (user == null) return; // don't reveal user existence

var token = await _userManager.GeneratePasswordResetTokenAsync(user);

var callbackUrl = Url.Page(

"/Account/ResetPassword",

values: new { userId = user.Id, token });

await _emailSender.SendAsync(user.Email!, "Reset Password",

$"Reset your password <a href='{callbackUrl}'>here</a>.");

3.在重置页面上,您将消耗该令牌:

var result = await _userManager.ResetPasswordAsync(user, token, model.NewPassword);

令牌是短暂存在的,由数据保护密钥签名和保护。

9.2 双因素认证 (2FA)

身份验证支持:

  • TOTP(身份验证器应用程序,例如 Microsoft Authenticator、Google Authenticator)。
  • 短信代码(如果您配置了短信服务提供商)。
  • 电子邮件验证码(强度较低,但比单因素验证要好)。

启用后,密码登录成功会触发额外的验证步骤,通常是通过SignInManager<TUser>.TwoFactorAuthenticatorSignInAsync(...)流程进行验证。

9.3 锁定和暴力破解保护

启用锁定功能后:

  • AccessFailedCount每次失败尝试都增加一个单位。
  • 一旦达到某个阈值MaxFailedAccessAttempts,账户将被锁定,直到LockoutEnd......

您应该始终启用密码登录锁定功能,尤其是在暴露于公共互联网时。

9.4 电子邮件发送和后台工作

在实际系统中,你不希望注册流程同步依赖于 SMTP。

稳健的架构:

  • 控制器或 Razor Pages 将电子邮件命令排入队列(例如 Azure 队列、RabbitMQ、后台工作进程)。
  • 后台服务会分别发送电子邮件并记录故障。

身份验证机制不会强制执行这些操作;您可以自行添加IEmailSender实现。

10. 检查清单:在 .NET 10 中强化 ASP.NET Core Identity

发布正式版应用时,请按照以下清单进行检查:

10.1 基础知识

  • 要求用户使用唯一的电子邮件地址。
  • 强制执行强密码策略(长度、复杂性、必要时可添加历史记录)。
  • 全部使用 HTTPS。将 HTTP 重定向到 HTTPS。
  • 配置 cookie 安全性:SecurePolicy = Always,,HttpOnly = true适当的SameSite

10.2 账户生命周期

  • 登录前(或执行敏感操作前)需要确认电子邮件地址。
  • 启用锁定功能并选择合理的锁定持续时间。
  • 实现密码重置流程并进行端到端测试。
  • 提供符合您隐私规则的帐户删除/停用路径。

10.3 授权设计

  • 保持角色小而稳定(Admin,,,...) SupportCustomer
  • 使用声明 + 策略来实现动态规则(subscription,,tenant_id功能标志)。
  • 集中进行政策登记;不要到处散布临时User.IsInRole检查。

10.4 多环境、多租户

  • TenantId(或等效的)数据存储在用户和/或声明中。
  • 使用策略/处理程序强制执行租户边界(例如SameTenantRequirement)。
  • 不要在不相关的租户或环境之间共享数据保护密钥。

10.5 可观测性和运行

  • 记录登录失败、锁定、密码重置尝试(不泄露机密信息)。
  • 监控异常模式(例如,来自较小 IP 范围的多次登录失败)。
  • 根据您的安全策略,定期轮换数据保护密钥和电子邮件/OTP 密钥。

最后想说的话

ASP.NET Core Identity不仅仅是在创建项目时勾选的"个人帐户"复选框。

当你把它当作一个安全子系统而不是"搭建框架"来对待时,你可以:

  • 构建丰富的用户画像和租户模型。
  • 通过声明和自定义处理程序来执行细粒度策略。
  • 以受控方式组合 cookie、令牌和外部身份提供商。
  • 放心使用 2FA、密码重置和锁定等功能。

如果你今天要开始开发一个新的**.NET 10**应用,请不要轻易使用默认设置。花几个小时进行设计:

  • 你的用户模型,
  • 您的角色/主张/政策,
  • 你的 cookie/令牌故事,以及
  • 您在身份验证方面的运营实践。

这些决策将在系统投入生产的每一天都带来回报。

祝您编码愉快!愿您的身份始终得到验证、授权和正确记录。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

相关推荐
俭朴的奇异果10 小时前
MongoDB实战开发 【零基础学习,附完整Asp.net示例】
学习·mongodb·asp.net
yuyuyui20 小时前
.NET CORE认证模块-注册方案与请求认证探究
认证·.net core
舒心的云朵1 天前
用三张图片详解Asp.Net 全生命周期
后端·asp.net
孝顺的书包1 天前
用Asp.net写自己的服务框架
后端·asp.net
糊涂的世界1 天前
IE10、IE11 User-Agent 导致的 ASP.Net 网站无法写入Cookie 问题
后端·asp.net
GV191rLvq2 天前
基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
服务器·前端·asp.net
乐观的夕阳2 天前
ASP.NET 异步页的实现方式
java·数据库·asp.net
管家婆客服中心2 天前
Server 2016系统安装IIS和ASP.NET框架
asp.net·server 2016
威武的花瓣2 天前
细说ASP.NET的各种异步操作
后端·asp.net·php
任性的芝麻2 天前
ASP.NET MVC 中的异步方式
后端·asp.net·mvc