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

.NET 8 + Kestrel如何同时支持Basic与Negotiate认证方案?

解决方案

1. 注册双认证方案(不指定单一默认)

在ConfigureServices中同时注册Negotiate和Basic认证,不通过AddAuthentication指定默认方案,转而通过授权策略允许两种认证方式:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication()
        .AddNegotiate()
        .AddBasic(options =>
        {
            options.Realm = "MyReallyRealm";
            options.SuppressWWWAuthenticateHeader = false;
            options.Events = new BasicAuthenticationEvents
            {
                OnValidateCredentials = ValidateCredentials
            };
        });

    // 配置全局授权策略,允许两种认证方案通过
    services.AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes("Negotiate", "Basic")
            .RequireAuthenticatedUser()
            .Build();
    });
}

2. 自定义挑战逻辑,返回多认证头

默认ASP.NET仅返回默认挑战方案的单个WWW-Authenticate头,需自定义处理程序在401时添加所有所需头:

首先扩展认证配置,添加自定义挑战方案:

services.AddAuthentication(options =>
{
    options.DefaultChallengeScheme = "MultiAuthChallenge";
})
.AddNegotiate()
.AddBasic(options =>
{
    options.Realm = "MyReallyRealm";
    options.SuppressWWWAuthenticateHeader = false;
    options.Events = new BasicAuthenticationEvents
    {
        OnValidateCredentials = ValidateCredentials
    };
})
// 注册自定义挑战处理程序
.AddScheme<AuthenticationSchemeOptions, MultiAuthChallengeHandler>("MultiAuthChallenge", null);

然后实现自定义挑战处理程序:

public class MultiAuthChallengeHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly IOptionsMonitor<BasicAuthenticationOptions> _basicOptions;

    public MultiAuthChallengeHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock,
        IOptionsMonitor<BasicAuthenticationOptions> basicOptions)
        : base(options, logger, encoder, clock)
    {
        _basicOptions = basicOptions;
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // 仅处理挑战,不参与认证逻辑
        return Task.FromResult(AuthenticateResult.NoResult());
    }

    protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = StatusCodes.Status401Unauthorized;
        
        // 添加Negotiate认证头
        Response.Headers.Append(HeaderNames.WWWAuthenticate, "Negotiate");
        // 添加带Realm的Basic认证头
        Response.Headers.Append(HeaderNames.WWWAuthenticate, 
            $"Basic realm=\"{_basicOptions.CurrentValue.Realm}\"");
        // 如需支持NTLM可添加此行
        // Response.Headers.Append(HeaderNames.WWWAuthenticate, "NTLM");

        await Task.CompletedTask;
    }
}

3. 确认中间件顺序

在Configure方法中确保认证、授权中间件顺序正确,且位于业务处理逻辑之前:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    // 认证中间件必须在授权中间件之前
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        // 适配无路由WebDAV的端点配置,或替换为自定义WebDAV处理中间件
        endpoints.Map("/", async context =>
        {
            await context.Response.WriteAsync("WebDAV Service");
        });
    });
}

效果验证

  • 客户端发送Basic认证请求时,Basic处理程序会触发OnValidateCredentials逻辑;
  • 客户端发送Negotiate/NTLM请求时,Negotiate处理程序会处理Windows认证;
  • 认证失败时,响应头会同时返回Negotiate和Basic realm="MyReallyRealm"(如需NTLM可自行添加)。

内容的提问来源于stack exchange,提问作者sawie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 20:00:15