ASP.NET Core JwtBearer 认证包实战:从 JWT 校验到事件扩展的完整指南
【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore
导读
Microsoft.AspNetCore.Authentication.JwtBearer是 ASP.NET Core 框架中负责JWT(JSON Web Token)Bearer 认证的核心中间件组件。本文以该包在aspnetcore仓库(包说明文档、源码目录)中的实现为主线,完整讲解其工作原理、配置项、事件钩子与实战用法。读完本文,你将能够:在 ASP.NET Core 应用(含最小 API)中快速接入 JWT 认证;理解令牌从Authorization请求头解析到校验、生成ClaimsPrincipal的全流程;掌握签发方/受众/签名密钥/有效期等TokenValidationParameters的每一项配置;并学会利用JwtBearerEvents在认证链的关键节点注入自定义逻辑(如从 Cookie 或查询参数读取令牌、审计登录、自定义 401 响应等)。
包概览与核心定位
JwtBearer 包是一个面向 API 与 Web 服务的无状态认证中间件:它不签发令牌,只负责验证由外部认证服务器(如 IdentityServer、Azure AD、自定义 STS)签发的 JWT。认证成功后将令牌中的声明(Claims)组装为ClaimsPrincipal,交由后续授权逻辑(如[Authorize]、RequireAuthorization())使用。
按仓库内 PACKAGE.md 的说明,其关键特性包括:
- 与 ASP.NET Core 应用无缝集成,通过
services.AddAuthentication(...).AddJwtBearer(...)一行接入; - 支持完整的 JWT 校验(签名、签发方、受众、有效期);
- 提供高度灵活的
TokenValidationParameters配置; - 支持 .NET Core 3.0 及更新版本,以及 .NET Standard 2.1。
包中还内置了两个可直接运行、用于验证 API 的示例:传统的 JwtBearerSample(使用Startup类 + OIDC 认证服务器)与最小 API 风格的 MinimalJwtBearerSample。
快速上手:最小配置示例
配置认证服务
以下配置来自包文档的官方示例(PACKAGE.md),演示了使用对称密钥(SymmetricSecurityKey)进行本地签名校验的经典写法:
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System.Text; public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, // 校验签发方 ValidateAudience = true, // 校验受众 ValidateLifetime = true, // 校验有效期(过期/未生效) ValidateIssuerSigningKey = true, // 校验签名密钥 ValidIssuer = "your_issuer", ValidAudience = "your_audience", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key")) }; }); // 其他配置... }注意:AddAuthentication中传入的JwtBearerDefaults.AuthenticationScheme即字符串"Bearer"(定义见 JwtBearerDefaults.cs),它表示默认认证方案,后续[Authorize]或RequireAuthorization()会默认走该方案。
完整的最小 API 示例
仓库中的 MinimalJwtBearerSample 展示了更贴近现代写法的接入方式,并且同时注册了多个命名方案:
using System.Security.Claims; var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication() .AddJwtBearer() .AddJwtBearer("ClaimedDetails") .AddJwtBearer("InvalidScheme"); builder.Services.AddAuthorization(options => options.AddPolicy("is_admin", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("is_admin", "true"); })); var app = builder.Build(); app.MapGet("/protected", (ClaimsPrincipal user) => $"Hello {user.Identity?.Name}!") .RequireAuthorization(); app.MapGet("/protected-with-claims", (ClaimsPrincipal user) => { return $"Glory be to the admin {user.Identity?.Name}!"; }) .RequireAuthorization("is_admin"); app.Run();该示例体现了三点实战技巧:
- 多方案注册:可多次调用
AddJwtBearer("SchemeName")注册多个独立方案,各自配置不同的校验参数; - 声明策略授权:
RequireClaim("is_admin", "true")直接基于令牌中的自定义声明做策略授权,无需额外查库; - 最小 API 直接注入:
ClaimsPrincipal user参数由框架自动注入,可直接读取user.Identity.Name。
集成 OIDC 认证服务器的典型配置
在实际生产中,JWT 通常由 OpenID Connect 认证服务器签发。此时无需手工指定签名密钥,只需配置Authority(权威端点)与Audience(受众),框架会自动从/.well-known/openid-configuration获取元数据与签名密钥。仓库示例 JwtBearerSample/Startup.cs 给出了这种模式:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(o => { // 需与前端 /wwwroot/app/scripts/app.js 中的配置保持一致 o.Authority = Configuration["oidc:authority"]; o.Audience = Configuration["oidc:clientid"]; });核心类型一览
包文档列出的四个主要类型(PACKAGE.md)如下,其中wtBearerOptions为文档笔误,实际类型名为JwtBearerOptions:
| 类型 | 文件 | 职责 |
|---|---|---|
JwtBearerDefaults | JwtBearerDefaults.cs | 提供默认认证方案名常量"Bearer" |
JwtBearerEvents | JwtBearerEvents.cs | 定义认证过程中的事件委托,供应用注入自定义处理 |
JwtBearerHandler | JwtBearerHandler.cs | 认证处理器核心,负责令牌提取、校验、挑战(Challenge)与禁止(Forbid) |
JwtBearerOptions | JwtBearerOptions.cs | 认证行为的全部可配置项 |
认证入口:AddJwtBearer 扩展方法
JwtBearerExtensions.cs 提供了 5 个重载,覆盖"无参默认方案 / 指定方案 / 指定方案+配置委托 / 指定方案+显示名+配置委托"等场景。其最完整的重载内部会:
- 注册
JwtBearerConfigureOptions(从IConfiguration绑定配置); - 注册
JwtBearerPostConfigureOptions(补全元数据地址、创建回退通道等默认行为); - 调用
AddScheme<JwtBearerOptions, JwtBearerHandler>(...)注册方案与处理器。
public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action<JwtBearerOptions> configureOptions) { builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<JwtBearerOptions>, JwtBearerConfigureOptions>()); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>()); return builder.AddScheme<JwtBearerOptions, JwtBearerHandler>(authenticationScheme, displayName, configureOptions); }JwtBearerOptions 全量配置详解
JwtBearerOptions.cs 继承自AuthenticationSchemeOptions,其公开属性按功能可分为四组,下表汇总了默认值,均以当前仓库源码为准:
1. 令牌校验参数
| 属性 | 默认值 | 说明 |
|---|---|---|
TokenValidationParameters | new TokenValidationParameters() | 校验 JWT 的核心参数对象(见下文专节) |
Audience | null | 期望的受众值;若TokenValidationParameters.ValidAudience为空,会被自动写入 |
MapInboundClaims | true | 是否将入站 JWT 声明名映射为 .NET 标准声明名(如name→http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name),同时同步到默认的JwtSecurityTokenHandler与JsonWebTokenHandler |
UseSecurityTokenValidators | false | false时使用TokenHandlers(异步、更快的JsonWebTokenHandler);true时回退到旧的SecurityTokenValidators(同步、JwtSecurityTokenHandler),当TokenValidatedContext.SecurityToken需要JwtSecurityToken类型时使用 |
2. 元数据发现与回退通道(OIDC 模式)
| 属性 | 默认值 | 说明 |
|---|---|---|
Authority | null | OIDC 权威端点,若未设置MetadataAddress,会自动拼接/.well-known/openid-configuration |
MetadataAddress | null | 元数据发现端点,优先级高于Authority |
RequireHttpsMetadata | true | 元数据地址是否必须为 HTTPS;仅在开发环境可设为false(否则启动即抛InvalidOperationException) |
Configuration | null | 直接提供的静态OpenIdConnectConfiguration,设置后不再走元数据发现与回退通道 |
ConfigurationManager | null | 负责元数据的获取、缓存与刷新;默认由MetadataAddress+Backchannel自动创建 |
BackchannelHttpHandler | null | 拉取元数据所用的HttpMessageHandler |
Backchannel | null | 拉取元数据的HttpClient,自动创建 |
BackchannelTimeout | 1 分钟 | 回退通道 HTTP 调用的超时时间 |
AutomaticRefreshInterval | ConfigurationManager默认值 | 元数据自动刷新间隔 |
RefreshInterval | ConfigurationManager默认值 | 元数据获取失败或显式请求刷新时的最小重试间隔 |
RefreshOnIssuerKeyNotFound | true | 遇到SecurityTokenSignatureKeyNotFoundException(签名密钥轮换)时自动请求刷新元数据,默认开启 |
3. 响应与令牌处理
| 属性 | 默认值 | 说明 |
|---|---|---|
Challenge | "Bearer"(即JwtBearerDefaults.AuthenticationScheme) | 写入WWW-Authenticate响应头的质询文本 |
IncludeErrorDetails | true | 校验失败时是否在WWW-Authenticate头中返回error/error_description;设false可避免向调用方泄露错误细节 |
SaveToken | true | 认证成功后是否把access_token存入AuthenticationProperties(供后续重放/转发) |
4. 事件
| 属性 | 默认值 | 说明 |
|---|---|---|
Events | new JwtBearerEvents() | 处理认证各阶段事件的对象,见下文专节 |
TokenValidationParameters 各校验开关
该对象由Microsoft.IdentityModel.Tokens提供,是令牌校验的"裁判",常用开关如下:
| 属性 | 作用 |
|---|---|
ValidateIssuer | 校验签发方是否在ValidIssuer/ValidIssuers中 |
ValidIssuer/ValidIssuers | 合法签发方(单个 / 多个) |
ValidateAudience | 校验受众是否在ValidAudience/ValidAudiences中 |
ValidAudience/ValidAudiences | 合法受众(单个 / 多个) |
ValidateLifetime | 校验nbf(生效时间)与exp(过期时间) |
ValidateIssuerSigningKey | 校验签名密钥 |
IssuerSigningKey/IssuerSigningKeys | 对称密钥或公钥(单个 / 多个,多个可支持密钥轮换) |
ClockSkew | 时钟偏移容忍量(默认约 5 分钟,缓解认证服务器与应用服务器时间差) |
配置选项的自动绑定
AddJwtBearer会自动注册 JwtBearerConfigureOptions.cs,它会把appsettings.json中Authentication:Schemes:Bearer等配置节(按方案名匹配)绑定到选项上。支持从配置直接读取Authority、MetadataAddress、Challenge、IncludeErrorDetails、MapInboundClaims、SaveToken、RequireHttpsMetadata、RefreshOnIssuerKeyNotFound、BackchannelTimeout、RefreshInterval以及TokenValidationParameters下的ValidateIssuer、ValidIssuer(s)、ValidateAudience、ValidAudience(s),并通过SigningKeys:Issuer/SigningKeys:Value数组自动构造对称签名密钥。这意味着密钥等敏感配置可安全存放于配置系统(配合用户机密/环境变量)。
认证处理流程:JwtBearerHandler 源码解析
JwtBearerHandler.cs 是理解 JwtBearer 内部机制的关键,其HandleAuthenticateAsync方法(L56-L220)完整展现了认证流水线:
- 触发
MessageReceived事件:先给应用机会从其它位置(如 Cookie、查询字符串)提供令牌或直接拒绝请求(L62-L65); - 从请求头提取令牌:若无事件提供的令牌,则读取
Authorization头,若以"Bearer "(不区分大小写)开头则截取其后内容并Trim();无头或无令牌时返回NoResult()(L74-L94); - 准备校验参数:
SetupTokenValidationParametersAsync会克隆TokenValidationParameters以避免并发请求间的竞态,并在使用ConfigurationManager时合并元数据中的ValidIssuers与IssuerSigningKeys(L239-L261); - 逐个校验器尝试:默认遍历
TokenHandlers调用ValidateTokenAsync;UseSecurityTokenValidators=true时遍历SecurityTokenValidators,先CanReadToken再ValidateToken。任一校验器成功即中断(L101-L147); - 校验成功:构造
ClaimsPrincipal,把ValidTo/ValidFrom写入Properties.ExpiresUtc/IssuedUtc,触发TokenValidated事件,若SaveToken=true则将令牌以access_token名义存入AuthenticationProperties,最后Success()(L149-L177); - 校验失败:汇总所有校验异常(单个或
AggregateException),触发AuthenticationFailed事件后返回Fail(...)(L180-L194); - 密钥轮换自动恢复:
RecordTokenValidationError在捕获SecurityTokenSignatureKeyNotFoundException且RefreshOnIssuerKeyNotFound=true时调用ConfigurationManager.RequestRefresh()刷新元数据(L222-L237)。
HandleChallengeAsync(L275-L346)则负责生成 401 响应:当IncludeErrorDetails且存在认证失败时,会依据异常类型生成符合 RFC 6750 的WWW-Authenticate: Bearer error="invalid_token", error_description="..."头。错误描述与异常类型一一对应,例如:
SecurityTokenExpiredException→The token expired at '...'SecurityTokenInvalidAudienceException→The audience '...' is invalidSecurityTokenInvalidIssuerException→The issuer '...' is invalidSecurityTokenSignatureKeyNotFoundException→The signature key was not found
HandleForbiddenAsync(L349-L367)在授权失败时返回 403 并触发Forbidden事件。
JwtBearerEvents:认证事件扩展点
JwtBearerEvents.cs 提供 5 个事件委托(均默认为空操作),覆盖认证全生命周期:
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
OnMessageReceived | 收到协议消息、提取令牌之前 | 从 Cookie/查询参数等其它位置获取令牌;拒绝特定令牌 |
OnTokenValidated | 令牌通过校验、ClaimsIdentity生成后 | 加载附加声明、写审计日志、二次校验、下发自定义令牌 |
OnChallenge | 返回 401 质询之前 | 自定义 401 响应体、改写WWW-Authenticate头 |
OnAuthenticationFailed | 令牌校验失败后 | 记录失败原因、返回自定义错误 |
OnForbidden | 授权失败返回 403 时 | 自定义 403 响应 |
各事件对应的上下文类型继承自ResultContext<JwtBearerOptions>,可通过设置context.Result干预默认行为。例如MessageReceivedContext.Token属性(MessageReceivedContext.cs)允许在事件中手动注入令牌;TokenValidatedContext.SecurityToken(TokenValidatedContext.cs)持有校验后的令牌对象;JwtBearerChallengeContext(JwtBearerChallengeContext.cs)暴露Error/ErrorDescription/ErrorUri/Handled供自定义质询。
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Events = new JwtBearerEvents { OnMessageReceived = context => { // 从查询字符串读取令牌(如 WebSocket / SignalR 场景) var accessToken = context.Request.Query["access_token"]; if (!string.IsNullOrEmpty(accessToken)) { context.Token = accessToken; } return Task.CompletedTask; }, OnTokenValidated = context => { // 令牌校验成功后写审计日志或补充声明 Console.WriteLine($"Token validated for {context.Principal.Identity?.Name}"); return Task.CompletedTask; }, OnChallenge = context => { // 自定义 401 响应内容 context.HandleResponse(); context.Response.StatusCode = 401; return context.Response.WriteAsJsonAsync(new { error = "unauthorized" }); } }; });注意:当context.HandleResponse()被调用时,处理器将跳过默认的 401 写入逻辑(见 JwtBearerChallengeContext.cs),因此需要自行设置状态码与响应内容。
元数据发现与配置后处理
JwtBearerPostConfigureOptions.cs 在配置阶段完成默认行为的兜底:
- 若
TokenValidationParameters.ValidAudience为空且设置了Audience,则自动写入(L23-L26); - 若未提供
ConfigurationManager:有静态Configuration则包装为StaticConfigurationManager;否则从Authority推导MetadataAddress(追加/.well-known/openid-configuration);校验 HTTPS 要求;创建带默认请求头(Microsoft ASP.NET Core JwtBearer handler)、10 MB 响应上限与超时设置的HttpClient;最后构造ConfigurationManager(L28-L66)。
这解释了为何只配置Authority就能完成 OIDC 模式的自动发现与签名密钥下载。
从源码构建与测试
仓库内 README.md 给出了构建与测试指引:在src/Security/Authentication目录下执行./build.cmd构建、./build.cmd -t运行测试,也可在测试项目目录下用dotnet test运行单个项目的测试。完整的源码构建流程可参考仓库 docs/BuildFromSource.md。
结语
Microsoft.AspNetCore.Authentication.JwtBearer以"轻配置、强校验、可扩展"的设计,成为 ASP.NET Core 生态中接入 JWT 认证的标准路径:本地对称密钥场景只需配置一组TokenValidationParameters;生产级 OIDC 场景则通过Authority/Audience实现元数据自动发现与密钥轮换自愈;而JwtBearerEvents五个事件钩子让开发者能够在认证链的任何关键节点注入自定义逻辑。理解 JwtBearerHandler 的认证流水线与其选项模型,即可在真实项目中游刃有余地定制安全认证方案。
【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考