.NET 7 API 使用 JWT 时是否仍需显式配置身份验证和授权中间件?
结论:取决于启动方式
在 .NET 7 中,使用 WebApplication.CreateBuilder(args) 创建应用时,框架会在检测到相关服务后自动添加身份验证和授权中间件,因此可以省略显式调用 UseAuthentication() 和 UseAuthorization()。Microsoft 的 Minimal API 身份验证和授权文档说明了这一行为。
题目中的代码使用的是:
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
这是传统的 Startup 启动方式,不适用 WebApplication 的自动添加规则。除非项目中的其他组件已经添加了这两个中间件,否则仍应在 Startup.Configure 中显式配置。
自动添加机制适用于 WebApplication 托管模型,与是否使用 JWT 无关。使用该托管模型的 Controller API 同样适用,不限于通过 MapGet 等方法定义的 Minimal API。
服务注册和中间件配置有什么区别?
| API | 作用 |
|---|---|
AddAuthentication()、AddJwtBearer() | 注册身份验证服务、JWT Bearer 处理程序及其配置 |
AddAuthorization() | 注册授权服务及策略 |
UseAuthentication() | 将身份验证中间件加入请求管道 |
UseAuthorization() | 将授权中间件加入请求管道 |
WebApplication 会检查服务容器,分别判断是否需要自动添加中间件:
- 检测到
IAuthenticationSchemeProvider,且应用没有显式调用UseAuthentication()时,自动添加身份验证中间件。 - 检测到
IAuthorizationHandlerProvider,且应用没有显式调用UseAuthorization()时,自动添加授权中间件。
这些服务通常分别由 AddAuthentication() 和 AddAuthorization() 注册,具体判断见 ASP.NET Core 7.0 的 WebApplicationBuilder 源码。满足上述条件时,即使省略调用,中间件仍会由框架添加并执行。
使用 WebApplication 时如何配置?
下面的示例通过自动添加机制保护 /private 接口:
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = builder.Configuration["Jwt:Authority"];
options.Audience = builder.Configuration["Jwt:Audience"];
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.MapGet("/public", () => Results.Ok("Public"));
app.MapGet("/private", () => Results.Ok("Authenticated"))
.RequireAuthorization();
app.Run();
项目需要引用与 .NET 7 兼容的 Microsoft.AspNetCore.Authentication.JwtBearer 包,并提供有效的 Jwt:Authority 和 Jwt:Audience 配置。示例假定令牌颁发方提供可用的元数据和签名密钥发现端点。自签发 JWT 的项目需要按自己的签发方式配置验证参数。
这里的 RequireAuthorization() 不能省略,因为注册身份验证和授权服务不会自动保护所有接口。Controller API 通常通过 [Authorize] 声明访问要求,也可以配置全局要求认证的 FallbackPolicy。相关说明见 Microsoft 的身份验证概述。
继续使用 Startup 时如何配置?
对于题目中的 UseStartup<Startup>(),应保留 ConfigureServices 中的 JWT 和授权服务注册,并在 Configure 中按以下顺序配置中间件:
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
这里假定 ConfigureServices 已调用 AddControllers()、AddAuthentication(...).AddJwtBearer(...) 和 AddAuthorization(),并完成 JWT 验证配置。
身份验证应先于授权执行,让授权中间件能够使用已经建立的用户身份;授权检查则应在端点执行前完成。Microsoft 的中间件文档给出了这一顺序。
如果删除这两个调用后应用仍然“正常”,仅凭给出的 Program.cs 无法确定原因。需要检查实际执行的 Startup.Configure、项目封装的中间件扩展方法,以及是否有 IStartupFilter 等组件代为添加中间件。官方启动文档说明,IStartupFilter 可以替应用添加中间件。
哪些情况下应该显式调用?
需要控制中间件顺序时,应显式调用。例如,启用 CORS 的应用可以这样配置:
app.UseRouting();
app.UseCors("ApiCors");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
这段代码适用于 WebApplication,前提是已注册名为 ApiCors 的 CORS 策略及 Controller 服务。Microsoft 的身份验证和授权文档也以 CORS 为例,说明了显式配置顺序的必要性。
如果自定义中间件需要读取认证后的 HttpContext.User,应将它放在 UseAuthentication() 之后;如果还需要在授权检查前参与处理,则应放在 UseAuthorization() 之前。
在 WebApplication 中显式调用这些方法后,框架会识别已添加的中间件,不再自动添加对应项。因此,可以保留这些调用来明确中间件顺序。
如何确认 JWT 保护确实生效?
仅测试携带有效 Token 能否访问接口还不够,因为公开接口也可能返回相同结果。应选择一个明确要求身份验证的接口,至少检查以下情况:
| 请求情况 | 默认 JWT Bearer 行为下的预期结果 |
|---|---|
| 未携带 Token | 401 Unauthorized |
| Token 过期、签名无效,或不满足配置的颁发方、受众要求 | 401 Unauthorized |
| Token 有效且满足授权要求 | 正常进入接口 |
| Token 有效,但不满足接口要求的角色或权限策略 | 403 Forbidden |
这些预期以未自定义认证事件或授权响应处理为前提。还应检查接口是否标注了 [AllowAnonymous],以及请求是否实际命中了预期的受保护端点。身份验证中的 challenge 和 forbid 行为可参见 Microsoft 身份验证概述。
这与 .NET 7 的默认身份验证方案变化有关吗?
.NET 7 还有一项容易与此混淆的变化:当应用只注册一个身份验证方案时,框架默认会将它作为默认方案。因此,单方案场景下可能不再需要向 AddAuthentication() 传入方案名称。多个方案并存时,则需要明确配置默认方案,或在授权策略中指定使用哪个方案。参见默认身份验证方案变更说明。
默认方案选择与自动添加中间件相互独立,不能据此判断传统 Startup 管道可以省略中间件。
自动添加机制是有意设计的行为,有官方文档和 .NET 7 源码依据。对于当前项目,简单的 WebApplication 管道可以采用自动配置;传统 Startup 管道或有顺序要求的应用,应显式配置。升级到更高版本时,应核对目标版本文档,并重新验证未登录、无效 Token 和权限不足时的请求结果。
备注:内容仅供参考。
