401 Authentication Error when SoapClient tries to

2019-04-26 18:56发布

问题:

My application routinely connects to a third-party server to fetch data via SOAP/WSDL:

$this->soap_client = new SoapClient("https://[the-domain]:443/[path]?wsdl", array(
    'trace'=>1,
    'login'=>$this->username,
    'password'=>$this->password,
    'exceptions' => true,
    'cache_wsdl' => WSDL_CACHE_NONE
)

Everything has been great for the last year, but they recently updated their WSDL file and now when the application tries to connect I'm getting the following two errors:

SoapClient::SoapClient(http://[the-domain]:80/[path]?xsd=1): failed to open stream: HTTP request failed! HTTP/1.1 401 Unauthorized

and

SoapClient::SoapClient(): I/O warning : failed to load external entity "http://[the-domain]:80/[path]?xsd=1"

When I look at the WSDL XML file, it appears that the offending unloadable file is the document schema file (schemaLocation) that it's trying to import: (from WSDL:)

<types>
<xsd:schema>
<xsd:import namespace="[irrelevant]" schemaLocation="http://[the-domain]:80/[path]?xsd=1"/>
</xsd:schema>
</types>

I've been beating my head against this for a while, and as far as I can tell, the issue is one of two things:

  1. When I load that schema URL in a browser (after browser authentication) it 302 redirects to a https url (and drops the port declaration). Is it possible that SOAP call won't follow the redirect when trying to import the schema?
  2. Given the fact that the error message is a 401 error - is it possible that the SOAP call is not passing credentials when trying to import the schema? The schema file requires the same authentication as the WSDL file, but maybe the server is not extending the authentication to the schema when it tries to import?

Assuming that it's the second issue, is there any way that I can force the system to use a different schema URL without downloading the WSDL file, editing it, and storing it/referencing it locally? If so I could try passing the credentials in the URL (http://username:password@domain....)?

If my only shot is to create a modified copy of both the WSDL and XSD schema file, so be it, but I'd love to hear if anyone has any thoughts that would let me avoid this (as the schema does change from time-to-time).

回答1:

It looks like the PHP SoapClient sticks to the same domain policy (including scheme) for sending the Basic Auth username and password in the WSDL request which is done to import the xsd file in the WDSL schema.

So if the WSDL url has a https scheme and the import has a http scheme, PHP is not sending the basic authentication info since the connection is not encrypted anymore when requesting the http import url (which would compromise the confidentiality of the authentication info).

However it seems that at least for some PHP versions (might be fixed in newer versions) the authentication problem persists even if the http url redirects to the https one (on the same domain). After the redirect to a secure URL with the same domain PHP could off course include the given Basic Auth info again.

Solution

In the end the only neat way I found to fix this was making the other party change their WSDL content to import a secure URL (https one) which has the same scheme, domain and port as the WDSL url itself.

Workaround

If that is not an option for you off course you could always go for the workaround which is to save the WSDL as well as the import(s) as local files and refer to the WDSL file instead of the URL. Off course this would also mean you would have to change the WSDL to import the right local file (instead of the http URL), and possibly other imports as well. Unfortunately this is the only workaround I know of in this case.


PHP Bug?

I also found this PHP bug report which might be related.



回答2:

I'd give cURL a try, it has an option for following redirections

How to post SOAP Request from PHP