我的应用程序定期连接到第三方服务器,通过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
)
去年一切都很好,但他们最近更新了WSDL文件,现在当应用程序尝试连接时,我收到了以下两个错误:
SoapClient::SoapClient(http://[the-domain]:80/[path]?xsd=1): failed to open stream: HTTP request failed! HTTP/1.1 401 Unauthorized
和
SoapClient::SoapClient(): I/O warning : failed to load external entity "http://[the-domain]:80/[path]?xsd=1"
当我查看WSDL XML文件时,似乎有问题的不可加载文件是它试图导入的文档模式文件(schemaLocation(:(来自WSDL:(
<types>
<xsd:schema>
<xsd:import namespace="[irrelevant]" schemaLocation="http://[the-domain]:80/[path]?xsd=1"/>
</xsd:schema>
</types>
我已经为此头疼了一段时间,据我所知,问题是两件事之一:
- 当我在浏览器中加载该模式URL时(在浏览器身份验证之后(,它将
302 redirects
转换为https
URL(并删除端口声明(。在尝试导入模式时,SOAP调用是否可能不遵循重定向 - 假设错误消息是401错误,那么SOAP调用在尝试导入模式时是否可能没有传递凭据?模式文件需要与WSDL文件相同的身份验证,但服务器在尝试导入时可能没有将身份验证扩展到模式
假设这是第二个问题,有没有任何方法可以强制系统使用不同的模式URL,而无需下载WSDL文件、编辑它并在本地存储/引用它?如果是这样,我可以尝试在URL中传递凭据(http://username:password@domain....
(?
如果我唯一的尝试是创建WSDL和XSD模式文件的修改副本,那就顺其自然吧,但我很想听听是否有人有任何想法可以让我避免这种情况(因为模式确实会不时更改(。
看起来PHP SoapClient坚持相同的域策略(包括方案(,用于在WSDL请求中发送基本身份验证用户名和密码,该请求用于在WDSL模式中导入xsd文件。
因此,如果WSDL url具有https
方案,而导入具有http
方案,则PHP不会发送基本的身份验证信息,因为在请求http
导入url时,连接不再加密(这将损害身份验证信息的机密性(。
然而,似乎至少对于某些PHP版本(可能在较新版本中得到修复(,即使http
url重定向到https
url(在同一域上(,身份验证问题仍然存在重定向到具有相同域的安全URL后,PHP可能会再次包含给定的基本身份验证信息
解决方案
最后,我发现解决这个问题的唯一巧妙方法是让另一方更改他们的WSDL内容,以导入一个安全的URL(https
(,该URL与WDSL URL本身具有相同的方案、域和端口。
变通办法
如果这对您来说不是一个选项,您可以随时选择变通方法,即将WSDL和导入保存为本地文件,并引用WDSL文件,而不是URL。当然,这也意味着您必须更改WSDL以导入正确的本地文件(而不是http
URL(,并且可能还需要其他导入。不幸的是,这是我所知道的在这种情况下唯一的解决方法。
PHP Bug
我还发现了这个PHP错误报告,它可能是相关的。
我会试试cURL,它有一个用于以下重定向的选项
如何从PHP 发布SOAP请求