.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。

10.2 账户生命周期

  • \] 登录前(或执行敏感操作前)需要确认电子邮件地址。

  • \] 实现密码重置流程并进行端到端测试。

10.3 授权设计

  • \] 保持角色小而稳定(`Admin`,,,...) `Support`。`Customer`

  • \] 集中进行政策登记;不要到处散布临时`User.IsInRole`检查。

  • \] 将`TenantId`(或等效的)数据存储在用户和/或声明中。

  • \] 不要在不相关的租户或环境之间共享数据保护密钥。

  • \] 记录登录失败、锁定、密码重置尝试(不泄露机密信息)。

  • \] 根据您的安全策略,定期轮换数据保护密钥和电子邮件/OTP 密钥。

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

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

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

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

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

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

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

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

相关推荐
云草桑18 小时前
.net AI开发04 第八章 引入RAG知识库与文档管理核心能力及事件总线
数据库·人工智能·microsoft·c#·asp.net·.net·rag
aiyo_2 天前
深入浅出DOTNET技术原理(二)coreclr和hostfxr剖析
.net·.net core
aiyo_6 天前
深入浅出DOTNET技术原理
.net·.net core
EdisonZhou6 天前
MAF快速入门(13)常见智能体编排模式
llm·agent·.net core
贾修行6 天前
IIS 作为反向代理:为 ASP.NET Core Kestrel 应用保驾护航
后端·iis·asp.net·反向代理·arr·url 重写规则
William_cl6 天前
C# ASP.NET强类型视图:让 UI 数据交互告别 “猜谜游戏“
ui·c#·asp.net
2501_940198698 天前
从“数据孤岛”到“智慧医脑”:实战 MCP 协议安全接入 HIS 系统,构建医疗级 AI 辅助诊断合规中台
人工智能·安全·asp.net
王柏龙9 天前
ASP.NET Core 框架原生健康检查服务详解
后端·asp.net
EdisonZhou11 天前
MAF快速入门(12)主工作流+子工作流
llm·aigc·agent·.net core