如何添加对Kestrel主机的NTLM支持?

时间:2019-10-10 08:40:51

标签: c# asp.net-core ntlm kestrel

我们想使用Kestrel来托管我们的网络API。我们必须同时支持NTLM和协商身份验证。

Core 3.0应该可以做到 https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.0&tabs=visual-studio

但是,当红est响应挑战时,仅返回协商方案。有没有人设法通过Kestrel实现NTLM身份验证?

该应用程序在Windows 10计算机上运行

基本上,我们遵循了建议。首先向服务添加身份验证:

        services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();

然后将身份验证添加到管道

        app.UseAuthentication();

我们还在管道中拥有自己的中间件,以确保用户已经过验证

        app.UseMiddleware<ValidateAuthentication>();

实施看起来像这样

internal class ValidateAuthentication : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        if (context.User.Identity.IsAuthenticated)
            await next(context);
        else
            await context.ChallengeAsync();
    }
}

问题是挑战响应仅具有协商性

    WWW-Authenticate Negotiate

我本来希望NTLM和谈判

    WWW-Authenticate NTLM, Negotiate

1 个答案:

答案 0 :(得分:0)

您可以覆盖HandleChallengeAsync方法,然后替换处理程序:

public sealed class NtlmNegotiateHandler : NegotiateHandler
{
    public NtlmNegotiateHandler(
        IOptionsMonitor<NegotiateOptions> options, 
        ILoggerFactory logger, UrlEncoder encoder, 
        ISystemClock clock) : base(options, logger, encoder, clock)
    {
    }

    protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        await base.HandleChallengeAsync(properties);

        if (Response.StatusCode ==  StatusCodes.Status401Unauthorized)
        {
            Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.WWWAuthenticate, "NTLM");
        }
    }
}
public sealed class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddAuthentication(NegotiateDefaults.AuthenticationScheme)
            .AddNegotiate();

        // replace the handler
        var serviceDescriptor = new ServiceDescriptor(typeof(NegotiateHandler), 
                                                      typeof(NtlmNegotiateHandler), 
                                                      ServiceLifetime.Transient);

        services.Replace(serviceDescriptor);
    }
}