You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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个关键错误,导致认证未被正确触发:

  1. 认证/授权中间件位置颠倒:你将UseAuthentication()和UseAuthorization()放在了自定义中间件之后,ASP.NET Core要求认证必须在需要授权的组件之前执行,否则授权逻辑无法获取认证后的用户信息。
  2. 未明确应用授权策略:分支管道中没有触发BasicAuthentication授权策略的逻辑,导致认证流程完全被跳过。
  3. 多余的中间件重复调用:全局管道已添加认证授权中间件,分支管道和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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 08:55:17