目录
[1. 心智模型:ASP.NET Core Identity 的真正含义](#1. 心智模型:ASP.NET Core Identity 的真正含义)
[2. .NET 10 项目设置(精简但实用)](#2. .NET 10 项目设置(精简但实用))
[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 的真正含义
身份认证不仅仅是"登录页面"。可以把它想象成三个层次:
-
持久层(Entity Framework Core)
类似这样 的表格
AspNetUsers、``AspNetRoles、``AspNetUserClaims、``AspNetUserLogins...``UserManager<TUser>并RoleManager<TRole>协调读写操作。 -
身份验证层(Cookie/令牌)
登录成功后颁发身份验证 cookie(或持有者令牌)。
使用 ASP.NET Core 身份验证处理程序(cookie、JWT bearer 等)。 -
授权层(角色、声明、策略)
应用
[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 = Finance和IsManager = 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-- 防止数据库级别的更新丢失。LockoutEnd,AccessFailedCount-- 驱动锁定逻辑。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。
5.1 Cookie 方案结构
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 后端。
7.1 使用身份 cookie 保护最小 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 持有者 + 身份
如果您更喜欢代币:
- 使用身份管理进行用户管理。
- 使用自定义端点颁发 JWT
JwtSecurityTokenHandler。 - 使用以下方式保护 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 密码重置流程(概要)
- 用户在"忘记密码"页面提交电子邮件地址。
- 您生成一个令牌:
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。
10.2 账户生命周期
-
\] 登录前(或执行敏感操作前)需要确认电子邮件地址。
-
\] 实现密码重置流程并进行端到端测试。
10.3 授权设计
-
\] 保持角色小而稳定(`Admin`,,,...) `Support`。`Customer`
-
\] 集中进行政策登记;不要到处散布临时`User.IsInRole`检查。
-
\] 将`TenantId`(或等效的)数据存储在用户和/或声明中。
-
\] 不要在不相关的租户或环境之间共享数据保护密钥。
-
\] 记录登录失败、锁定、密码重置尝试(不泄露机密信息)。
-
\] 根据您的安全策略,定期轮换数据保护密钥和电子邮件/OTP 密钥。
ASP.NET Core Identity不仅仅是在创建项目时勾选的"个人帐户"复选框。
当你把它当作一个安全子系统而不是"搭建框架"来对待时,你可以:
- 构建丰富的用户画像和租户模型。
- 通过声明和自定义处理程序来执行细粒度策略。
- 以受控方式组合 cookie、令牌和外部身份提供商。
- 放心使用 2FA、密码重置和锁定等功能。
如果你今天要开始开发一个新的**.NET 10**应用,请不要轻易使用默认设置。花几个小时进行设计:
- 你的用户模型,
- 您的角色/主张/政策,
- 你的 cookie/令牌故事,以及
- 您在身份验证方面的运营实践。
这些决策将在系统投入生产的每一天都带来回报。
祝您编码愉快!愿您的身份始终得到验证、授权和正确记录。
如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。