简体   繁体   English

每个请求的C#WebClient NTLM身份验证启动

[英]C# WebClient NTLM authentication starting for each request

Consider a simple C# NET Framework 4.0 application, that: 考虑一个简单的C#.NET Framework 4.0应用程序,它:

  • uses WebClient 使用WebClient
  • authenticates using NTLM (tested on IIS 6.0 and IIS 7.5 server) 使用NTLM进行身份验证(在IIS 6.0和IIS 7.5服务器上测试)
  • retrieves a string from an URL multiple times using DownloadString() 使用DownloadString()多次从URL检索字符串

Here's a sample that works fine: 这是一个工作正常的样本:

using System;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string URL_status = "http://localhost/status";

            CredentialCache myCache = new CredentialCache();
            myCache.Add(new Uri(URL_status), "NTLM", new NetworkCredential("username", "password", "domain"));

            WebClient WebClient = new WebClient();
            WebClient.Credentials = myCache;

            for (int i = 1; i <= 5; i++)
            {
                string Result = WebClient.DownloadString(new Uri(URL_status));
                Console.WriteLine("Try " + i.ToString() + ": " + Result);
            }

            Console.Write("Done");
            Console.ReadKey();
        }
    }
}

The problem: 问题:

When enabling tracing I see that the NTLM authentication does not persist. 启用跟踪时,我发现NTLM身份验证不会持久存在。

Each time Webclient.DownloadString is called, NTLM authentication starts (server returns "WWW-Authenticate: NTLM" header and the whole authenticate/authorize process repeats; there is no "Connection: close" header). 每次调用Webclient.DownloadString时,NTLM身份验证都会启动(服务器返回“WWW-Authenticate:NTLM”标头,并且整个身份验证/授权过程重复;没有“Connection:close”标头)。

Wasn't NTLM supposed to authenticate a connection, not a request? 是不是NTLM应该验证连接,而不是请求?

Is there a way to make WebClient reuse an existing connection to avoid having to re-authenticate each request? 有没有办法让WebClient重用现有连接以避免重新验证每个请求?

After 10 days of trying everything I could think of and learning a lot in the process, I finally figured a fix for this issue. 经过10天尝试我能想到的一切并在此过程中学到很多东西,我终于找到了解决这个问题的方法。

The trick is to enable UnsafeAuthenticatedConnectionSharing by overriding GetWebRequest and setting the property to true in the HttpWebRequest you get back. 诀窍是通过重写GetWebRequest并在您返回的HttpWebRequest中将属性设置为true来启用UnsafeAuthenticatedConnectionSharing

You may want to combine that with the ConnectionGroupName property to avoid the potential use of the connection by unauthenticated applications. 您可能希望将其与ConnectionGroupName属性组合,以避免未经身份验证的应用程序对连接的潜在使用。

Here is the sample from the question modified to work as expected. 以下是修改后的问题样本按预期工作。 It opens a single NTLM authenticated connection and reuses it: 它打开一个NTLM身份验证连接并重用它:

using System;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string URL_status = "http://localhost/status";

            CredentialCache myCache = new CredentialCache();
            myCache.Add(new Uri(URL_status), "NTLM", new NetworkCredential("username", "password", "domain"));

            MyWebClient webClient = new MyWebClient();
            webClient.Credentials = myCache;

            for (int i = 1; i <= 5; i++)
            {
                string result = webClient.DownloadString(new Uri(URL_status));
                Console.WriteLine("Try {0}: {1}", i, result);
            }

            Console.Write("Done");
            Console.ReadKey();
        }
    }

    public class MyWebClient : WebClient
    {
        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest request = base.GetWebRequest(address);

            if (request is HttpWebRequest) 
            {
                var myWebRequest = request as HttpWebRequest;
                myWebRequest.UnsafeAuthenticatedConnectionSharing = true;
                myWebRequest.KeepAlive = true;
            }

            return request;
        }
    }   
}

At this point I would also like to thank @Falco Alexander for all the help; 在这一点上,我还要感谢@Falco Alexander的所有帮助; while his suggestions didn't quite work for me, he did point me in the right direction to look for and finally find the answer. 虽然他的建议对我不起作用,但他确实指出了我正确的方向寻找并最终找到答案。

Check your IIS's setting, though that should be default. 检查IIS的设置,但这应该是默认设置。

<windowsAuthentication
   enabled="True"
   authPersistSingleRequest="False"
   UseKernelMode>

</windowsAuthentication>  

Ref: https://msdn.microsoft.com/en-us/library/aa347472.aspx 参考: https//msdn.microsoft.com/en-us/library/aa347472.aspx

Did you check the zone your localhost IIS is in? 您是否检查了localhost IIS所在的区域? This was also a pitfall from the client side in the past when working with WinInet. 在使用WinInet时,这也是客户端的一个陷阱。 Check that default behaviour of WebClient . 检查WebClient默认行为。


Edit: 编辑:

After reproducing the error, I could figure out it's the missing NTLM preauthentication implementation of WebClient that keeps you from a single 401 request: 在重现错误之后,我可以发现它是WebClient缺少的NTLM 预身份验证实现,它使您免于单个401请求:

var WebClient = new PreAuthWebClient();
WebClient.Credentials = new NetworkCredential("user", "pass","domain");

//Do your GETs 

Public class PreAuthWebClient: WebClient
{
    protected override WebRequest GetWebRequest (Uri address)
    {
        WebRequest request = (WebRequest) base.GetWebRequest (address);
        request.PreAuthenticate = true;
        return request;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM