简体   繁体   中英

(407) Proxy Authentication Required - Basic Authentication

Edit:

After struggling for a long time to figure this out, I came across a potential solution. As of today (2021-10-19), the latest stable version of System.ServiceModel.*** packages is 4.8.1, but there are release candidates for 4.9.0 which seem to solve exactly the problem I'm having here.

I checked the .NET WCF GitHub source and found this release candidate (version 4.9.0-rc1.21431.2 ) which has exactly what I'm looking for. They've updated the HttpTransportBindingElement to include a Proxy property . Obviously it is not stable release yet, but it still gets the job done. With that I was able to solve the original problem using something that looks like this:

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
    };

    /* 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!!!
}

Original question:

I'm trying to send a WCF service request through a web proxy, and I'm receiving the error "Remote Server returned an error: (407) Proxy Authentication Required". I've already generated the proxy classes with a WSDL, set up the bindings/endpoints etc. in my app.config (it is a BasicHttpBinding ). The problem is: both the client and the proxy require Basic authentication, and I can only seem be able to set the client credentials, not the proxy.

Things I've already tried:

  1. I saw online you could try to pass credentials in the URL of the proxy itself. So I did this programatically for the ProxyAddress property on the binding, like so:
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. I also tried with default web proxy (it sorta worked). Again, I set it programatically like so:
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;
    }
}

The good thing about this second approach is that it actually worked! However, it is not enough for the requirements of my project. The application I am developing is sending loads of requests per second on different threads, some of which are going through the default proxy. I don't want all those unrelated requests to go through my "new" proxy, they should continue to go through the default.

So to summarize, I need a way of setting the proxy per-request , while also being able to set Basic authentication for both the client and the proxy. I'm not very experienced with WCF and I have just stumbled along the concept of " Custom bindings ", which seems promising, but I still haven't found if it can do what I need. Any help on this is incredibly appreciated!

Welcome to Stack Overflow. Thanks for your detailed question.

The "proper" solution is to use an HTTPS proxy (not an HTTP proxy).

If this isn't feasible, you can set the Binding's security mode to BasicHttpSecurityMode.TransportCredentialOnly . (Because Basic Authentication isn't encrypted, I don't recommend doing this in a Production application.)

Below is an example based on your original post. Let me know if it works for you.

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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