(407) 需要代理身份验证 - 基本身份验证

Posted

技术标签:

【中文标题】(407) 需要代理身份验证 - 基本身份验证【英文标题】:(407) Proxy Authentication Required - Basic Authentication 【发布时间】:2021-10-10 21:19:48 【问题描述】:

编辑:

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

我检查了 .NET WCF GitHub 源代码,发现 this release candidate(版本 4.9.0-rc1.21431.2)正是我想要的。他们更新了HttpTransportBindingElement 以包含Proxy property。显然它还不是稳定版本,但它仍然可以完成工作。这样,我就可以使用如下所示的方法解决原始问题:

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)。问题是:客户端和代理都需要基本身份验证,而我似乎只能设置客户端凭据,而不是代理。

我已经尝试过的事情:

    我在网上看到您可以尝试在代理本身的 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

    我还尝试了使用默认 Web 代理(它有点工作)。同样,我以编程方式将其设置为:
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 不是很有经验,我只是偶然发现了“Custom bindings”的概念,这似乎很有希望,但我仍然没有发现它是否可以满足我的需求。非常感谢您对此的任何帮助!

【问题讨论】:

根据您的需要,您可以尝试过滤器吗? 【参考方案1】:

欢迎来到 Stack Overflow。感谢您的详细问题。

“正确”的解决方案是使用 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);

【讨论】:

谢谢,但你是对的,正如你提到的,这不适用于生产解决方案,基本凭据未加密(我们不希望这些凭据泄露)。你会碰巧知道如何在 WCF 中使用 HTTPS 代理吗?我发现的大多数地方似乎都表明它不受 .NET Framework 的支持。 您应该可以设置“binding.Security.Mode = BasicHttpSecurityMode.Transport”或“TransportWithMessageCredential”。这将允许通过 HTTPS 进行通信。 docs.microsoft.com/en-us/dotnet/api/…

以上是关于(407) 需要代理身份验证 - 基本身份验证的主要内容,如果未能解决你的问题,请参考以下文章

407 需要代理身份验证

C# 407 需要代理身份验证

在需要身份验证但不返回 407 的代理后面打开 http 连接

Firebug 控制台错误 HTTP 407 需要代理身份验证

远程服务器返回错误:(407) 需要代理身份验证

远程服务器返回错误:(407) 需要代理身份验证