为何ASP.NET Core中EndPoints认证生效,MapWhen却无效?
问题:ASP.NET Core中MapWhen无法触发Basic认证,EndPoints却正常工作
我在WebApplication中添加了BasicAuthentication,使用EndPoints时认证功能正常,但使用MapWhen时完全无效——已在多处添加UseAuthentication和UseAuthorization,却毫无作用。为何MapWhen无法生效?
我的MapWhen逻辑仅判断请求路径是否以<rootPath>开头,若是则使用对应的中间件。以下是相关代码:
中间件及扩展代码
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; namespace IdentiyService { public static class JsonServiceMiddlewareExtensions { public static IApplicationBuilder UseMockJson( this IApplicationBuilder builder , string rootPath, IWebHostEnvironment env, string jsonFile) { #if USE_ENDPOINTS // EndPoints模式下正常工作 IApplicationBuilder x = builder.UseEndpoints(endpoints => { RequestDelegate pipeline = endpoints.CreateApplicationBuilder() .UseMiddleware<JsonServiceMiddleware>(env, jsonFile) .Build(); endpoints .Map(rootPath, pipeline) .WithDisplayName("MockJson"); }); #else // MapWhen模式下完全无效 IApplicationBuilder x = MapWhenExtensions.MapWhen(builder, ctx => ctx.Request.Path.StartsWithSegments(rootPath, System.StringComparison.InvariantCultureIgnoreCase), app => { IApplicationBuilder fsck = app.UseRouting().UseAuthentication().UseAuthorization(); IApplicationBuilder foo = UseMiddlewareExtensions .UseMiddleware<JsonServiceMiddleware>(fsck, env, jsonFile); foo.UseAuthentication().UseAuthorization(); } ); x = x.UseAuthentication().UseAuthorization(); #endif return x; } } public class JsonServiceMiddleware { private readonly RequestDelegate next; private readonly IWebHostEnvironment m_env; private readonly string m_jsonFile; public JsonServiceMiddleware(RequestDelegate next, IWebHostEnvironment env, string jsonFile) { this.next = next; this.m_env = env; this.m_jsonFile = jsonFile; } [Middleware.BasicAuthorization()] // 该特性仅在EndPoints模式下生效 public async Task InvokeAsync(HttpContext context) { RouteData route = context.GetRouteData(); string name = (string)route.Values["name"] ?? Path.GetFileNameWithoutExtension(this.m_jsonFile); name = name.ToLowerInvariant(); string outputPath = Path.Combine(m_env.ContentRootPath, "json", name + ".json"); outputPath = Path.GetFullPath(outputPath); if (!outputPath.StartsWith(m_env.ContentRootPath)) { context.Response.StatusCode = (int)HttpStatusCode.UnavailableForLegalReasons; return; } if (!File.Exists(outputPath)) { context.Response.StatusCode = (int)HttpStatusCode.BadRequest; return; } string output = File.ReadAllText(outputPath, Encoding.UTF8); context.Response.StatusCode = 200; context.Response.ContentType = "application/json; charset=utf-8"; await context.Response.WriteAsync(output); } } }
Startup.cs代码
using IdentiyService.Middleware; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IdentiyService { public class Startup { public IConfiguration Configuration { get; } public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddAuthentication().AddScheme< AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", options => { }); services.AddAuthorization(options => { options.AddPolicy("BasicAuthentication", new AuthorizationPolicyBuilder("BasicAuthentication") .RequireAuthenticatedUser() .Build() ); }); services.AddResponseCompression(options => { options.EnableForHttps = true; options.Providers.Add(new BrotliCompressionProvider()); options.Providers.Add(new GzipCompressionProvider()); options.Providers.Add(new DeflateCompressionProvider()); options.MimeTypes = new[] { "text/plain", "text/html", "text/css", "text/csv" ,"application/javascript", "application/json", "application/xml" ,"image/x-icon", "image/png", "image/gif", "image/jpeg", "image/webp", "image/tiff", "image/svg+xml" }; }); services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "IdentiyService", Version = "v1" }); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "IdentiyService v1")); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); // MapWhen模式下无效的调用 app.UseMockJson("/api/jid/v1/buildings", env, "buildings.json").UseAuthentication().UseAuthorization(); app.UseMockJson("/api/jid/v1/units", env, "units.json").UseAuthentication().UseAuthorization(); app.UseMockJson("/api/jid/v1/users", env, "users.json").UseAuthentication().UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapSomeMiddleware("/foo").RequireAuthorization("BasicAuthentication"); }); } } }
原因与解决方案
核心问题:中间件顺序与管道配置错误
MapWhen分支管道的配置存在3个关键错误,导致认证未被正确触发:
- 认证/授权中间件位置颠倒:你将
UseAuthentication()和UseAuthorization()放在了自定义中间件之后,ASP.NET Core要求认证必须在需要授权的组件之前执行,否则授权逻辑无法获取认证后的用户信息。 - 未明确应用授权策略:分支管道中没有触发
BasicAuthentication授权策略的逻辑,导致认证流程完全被跳过。 - 多余的中间件重复调用:全局管道已添加认证授权中间件,分支管道和
UseMockJson返回值后重复调用属于无效操作。
修复后的代码
1. 修改JsonServiceMiddlewareExtensions中的MapWhen分支
#else // 修复后的MapWhen配置 IApplicationBuilder x = MapWhen(builder, ctx => ctx.Request.Path.StartsWithSegments(rootPath, StringComparison.InvariantCultureIgnoreCase), app => { // 严格遵循中间件顺序:认证 → 授权 → 自定义中间件 app.UseAuthentication(); app.UseAuthorization(); // 显式触发授权策略,未通过则直接返回401 app.Use(async (context, next) => { var authResult = await context.AuthorizeAsync("BasicAuthentication"); if (!authResult.Succeeded) { context.Response.StatusCode = StatusCodes.Status401Unauthorized; return; } await next(); }); // 最后添加自定义中间件 app.UseMiddleware<JsonServiceMiddleware>(env, jsonFile); } ); #endif
2. 简化Startup中的调用代码
移除UseMockJson后的多余认证授权调用,全局管道已配置相关中间件:
// 修复后的调用方式 app.UseMockJson("/api/jid/v1/buildings", env, "buildings.json"); app.UseMockJson("/api/jid/v1/units", env, "units.json"); app.UseMockJson("/api/jid/v1/users", env, "users.json");
额外说明
- 若要通过
[Middleware.BasicAuthorization]特性触发认证,需确保该特性对应的中间件被添加到自定义中间件之前,且在分支管道中正确执行。 - MapWhen创建的是独立分支管道,全局管道的中间件会先执行,但分支管道内的中间件顺序仍需严格遵循ASP.NET Core规则:异常处理 → HTTPS重定向 → 路由 → 认证 → 授权 → 自定义中间件。
内容的提问来源于stack exchange,提问作者Stefan Steiger
相关产品推荐
相关产品推荐

