首页 > 解决方案 > (407) 需要代理身份验证 - 基本身份验证

问题描述

编辑:

经过很长时间的努力解决这个问题,我遇到了一个潜在的解决方案。截至今天(2021 年 10 月 19 日),System.ServiceModel.***软件包的最新稳定版本是 4.8.1,但有 4.9.0 的候选版本似乎完全解决了我在这里遇到的问题。

我检查了 .NET WCF GitHub 源,发现这个候选版本(版本4.9.0-rc1.21431.2)正是我正在寻找的。他们已经更新了HttpTransportBindingElement包含一个Proxy属性。显然它还不是稳定的版本,但它仍然可以完成工作。有了这个,我能够使用如下所示的东西来解决原始问题:

using (var myWsdlClient = new MyWsdlGeneratedClient())
{
    var binding = myWsdlClient.Endpoint.Binding as BasicHttpBinding;
    var customBinding = new CustomBinding(binding);

    var htbe = customBinding.Elements.Find<HttpTransportBindingElement>();
    htbe.AuthenticationScheme = AuthenticationSchemes.Basic;
    htbe.ProxyAuthenticationScheme = AuthenticationSchemes.Basic;
    htbe.UseDefaultWebProxy = false;
    htbe.BypassProxyOnLocal = false;
    htbe.Proxy = new WebProxy
    {
        Address = new Uri("http://myproxyaddress.com:8080"),
        /* Proxy creds */
        Credentials = new NetworkCredential("MyProxyUserName", "MyProxyPassword"),
        BypassProxyOnLocal = false
    };

    myWsdlClient.Endpoint.Binding = customBinding;

    /* Client creds */
    myWsdlClient.ClientCredentials.UserName.UserName = "MyClientUserName";
    myWsdlClient.ClientCredentials.UserName.Password = "MyClientPassword";

    /* Send request */
    myWsdlClient.Endpoint.Address = new EndpointAddress("https://myclientaddress.com");
    myWsdlClient.doSomeAction(actionRequest); // <-- IT WORKS!!!
}

原始问题:

我正在尝试通过 Web 代理发送 WCF 服务请求,并且收到错误“远程服务器返回错误:(407) 需要代理身份验证”。我已经使用 WSDL 生成了代理类,在我的 app.config 中设置了绑定/端点等(它是一个BasicHttpBinding. 问题是:客户端和代理都需要基本身份验证,我似乎只能设置客户端凭据,而不是代理。

我已经尝试过的事情:

  1. 我在网上看到您可以尝试在代理本身的 URL 中传递凭据。所以我以编程方式为ProxyAddress绑定上的属性执行此操作,如下所示:
using (var myWsdlClient = new MyWsdlGeneratedClient())
{
    var binding = myWsdlClient.Endpoint.Binding as BasicHttpBinding;

    /* Client creds */
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
    myWsdlClient.ClientCredentials.UserName.UserName = "MyClientUserName";
    myWsdlClient.ClientCredentials.UserName.Password = "MyClientPassword";

    /* Proxy creds */
    binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
    binding.UseDefaultWebProxy = false;
    binding.BypassProxyOnLocal = false;
    binding.ProxyAddress = new Uri("http://MyProxyUserName:MyProxyPassword@myproxyaddress.com:8080");

    /* Send request */
    myWsdlClient.Endpoint.Address = new EndpointAddress("https://myclientaddress.com");
    myWsdlClient.doSomeAction(actionRequest); // <-- error is thrown here, inner exception is 407 HTTP response
}
  1. 我也尝试使用默认的网络代理(它有点工作)。同样,我以编程方式设置它,如下所示:
using (var myWsdlClient = new MyWsdlGeneratedClient())
{
    var binding = myWsdlClient.Endpoint.Binding as BasicHttpBinding;

    /* Client creds */
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
    myWsdlClient.ClientCredentials.UserName.UserName = "MyClientUserName";
    myWsdlClient.ClientCredentials.UserName.Password = "MyClientPassword";

    /* Proxy creds */
    binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
    binding.UseDefaultWebProxy = true;
    binding.BypassProxyOnLocal = false;

    var defaultProxyBefore = WebRequest.DefaultWebProxy;
    var newProxy = new WebProxy
    {
        Address = new Uri("http://myproxyaddress.com:8080"),
        Credentials = new NetworkCredential("MyProxyUserName", "MyProxyPassword"),
        BypassProxyOnLocal = false
    };
    WebRequest.DefaultWebProxy = newProxy;

    /* Send request */
    myWsdlClient.Endpoint.Address = new EndpointAddress("https://myclientaddress.com");
    try
    {
        myWsdlClient.doSomeAction(actionRequest);
    }
    finally
    {
        WebRequest.DefaultWebProxy = defaultProxyBefore;
    }
}

第二种方法的好处是它确实有效!但是,对于我的项目的要求,这还不够。我正在开发的应用程序每秒在不同线程上发送大量请求,其中一些通过默认代理。我不希望所有那些不相关的请求都通过我的“新”代理,它们应该继续通过默认值。

总而言之,我需要一种按请求设置代理的方法,同时还能够为客户端和代理设置基本身份验证。我对 WCF 不是很有经验,我只是偶然发现了“自定义绑定”的概念,这似乎很有希望,但我仍然没有发现它是否可以满足我的需求。对此的任何帮助都非常感谢!

标签: c#.netwcfproxywcf-binding

解决方案


欢迎来到堆栈溢出。感谢您的详细问题。

“正确”的解决方案是使用 HTTPS 代理(不是 HTTP 代理)。

如果这不可行,您可以将 Binding 的安全模式设置为BasicHttpSecurityMode.TransportCredentialOnly. (因为基本身份验证未加密,我不建议在生产应用程序中这样做。)

以下是基于您的原始帖子的示例。请让我知道这对你有没有用。

using (var myWsdlClient = new MyWsdlGeneratedClient())
{
    var binding = myWsdlClient.Endpoint.Binding as BasicHttpBinding;
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

    /* Client creds */
    myWsdlClient.ClientCredentials.UserName.UserName = "MyClientUserName";
    myWsdlClient.ClientCredentials.UserName.Password = "MyClientPassword";

    /* Disable HTTPS requirement */
    binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;

    /* Proxy creds */
    /*
     * Since the credentials for the Proxy are in the URL, 
     *   set the proxy credential type to None (the default value).
     * Otherwise, WCF may attempt using myWsdlClient.ClientCredentials to
     *   authenticate with the Proxy.
     */
    binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;

    /* Note: UseDefaultWebProxy is true by default. */
    binding.UseDefaultWebProxy = false;
    binding.BypassProxyOnLocal = false;

    /* Ensure your Proxy Server supports passing credentials in the URL. */
    binding.ProxyAddress = new Uri("http://MyProxyUserName:MyProxyPassword@myproxyaddress.com:8080");

    /* Send request */
    myWsdlClient.Endpoint.Address = new EndpointAddress("https://myclientaddress.com");
    myWsdlClient.doSomeAction(actionRequest);
}

推荐阅读