使用 PHP 连接到受 WS-Security 保护的 Web 服务
我正在尝试连接到受密码保护且 url 为 https 的 Web 服务。 我不知道如何在脚本发出请求之前进行身份验证。 似乎我一定义服务它就发出请求。 例如,如果我输入:
$client = new SoapClient("https://example.com/WSDL/nameofservice",
array('trace' => 1,)
);
然后在浏览器上访问该站点,我会得到:
Fatal error: Uncaught SoapFault exception:
[WSDL] SOAP-ERROR: Parsing WSDL: Couldn't load from
'https://example.com/WSDL/nameofservice' in /path/to/my/script/myscript.php:2
Stack trace: #0 /path/to/my/script/myscript.php(2):
SoapClient->SoapClient('https://example...', Array) #1 {main} thrown in
/path/to/my/script/myscript.php on line 2
如果我尝试将服务定义为 Soap 服务器,例如:
$server= new SoapServer("https://example.com/WSDL/nameofservice");
我得到:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>WSDL</faultcode>
<faultstring>
SOAP-ERROR: Parsing WSDL:
Couldn't load from 'https://example.com/WSDL/nameofservice'
</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
我还没有尝试发送原始请求信封来查看服务器返回的内容,但这可能是一种解决方法。 但我希望有人能告诉我如何使用 php 内置类进行设置。 我尝试将“userName”和“password”添加到数组中,但这并不好。 问题是我什至无法判断我是否到达了远程站点,更不用说它是否拒绝请求了。
I am trying to connect to a Web Service which is password protected and the url is https. I can't figure out how to authenticate before the script makes a request. It seems like it makes a request as soon as I define the service. For instance, if I put in:
$client = new SoapClient("https://example.com/WSDL/nameofservice",
array('trace' => 1,)
);
and then go to the site on the browser, I get:
Fatal error: Uncaught SoapFault exception:
[WSDL] SOAP-ERROR: Parsing WSDL: Couldn't load from
'https://example.com/WSDL/nameofservice' in /path/to/my/script/myscript.php:2
Stack trace: #0 /path/to/my/script/myscript.php(2):
SoapClient->SoapClient('https://example...', Array) #1 {main} thrown in
/path/to/my/script/myscript.php on line 2
If I try defining the service as a Soap Server, like:
$server= new SoapServer("https://example.com/WSDL/nameofservice");
I get:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>WSDL</faultcode>
<faultstring>
SOAP-ERROR: Parsing WSDL:
Couldn't load from 'https://example.com/WSDL/nameofservice'
</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I haven't tried sending a raw request envelope yet to see what the server returns, but that may be a workaround. But I was hoping someone could tell me how I can set it up using the php built-in classes. I tried adding "userName" and "password" to the array, but that was no good. The problem is that I can't even tell if I'm reaching the remote site at all, let alone whether it is refusing the request.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
只需扩展 SoapHeader 即可创建符合 Wsse 的身份验证:
如果您需要使用带有随机数和时间戳的 ws-security,Peter 已在 http://php.net/manual/en/soapclient.soapclient.php#114976 他写道这对他有用:
比较以及答案中给出的详细信息 https://stackoverflow.com/a/18575154/367456
Simply extend the SoapHeader to create a Wsse compilant authentication:
If you need to use ws-security with a nonce and a timestamp, Peter has posted an update version on http://php.net/manual/en/soapclient.soapclient.php#114976 of which he wrote that it did work for him:
compare as well with the details given in answer https://stackoverflow.com/a/18575154/367456
问题似乎是 WSDL 文档受到某种保护(基本身份验证 - 我认为
SoapClient
不支持摘要身份验证,因此在这种情况下您会运气不好)并且因此,SoapClient 无法读取和解析服务描述。首先,您应该尝试在浏览器中打开 WSDL 位置,检查是否显示身份验证对话框。 如果存在身份验证对话框,您必须确保
SoapClient
在检索 WSDL 文档时使用所需的登录凭据。 问题是 SoapClient 只会发送通过login
和password
选项给出的凭据(以及local_cert
> 使用证书身份验证时的选项)在调用服务时创建客户端,而不是在获取 WSDL 时创建客户端(请参阅 此处)。 有两种方法可以解决此问题:将登录凭据添加到
SoapClient
构造函数调用上的 WSDL URL这应该是最简单的解决方案 - 但在 PHP 中 Bug #27777 据说这也不起作用(我还没有尝试过)。
ext/curl
手动获取 WSDL,或者通过浏览器或通过wget
手动获取 WSDL,例如,将其存储在磁盘上并实例化SoapClient< /code> 带有对本地 WSDL 的引用。
如果 WSDL 文档发生更改,此解决方案可能会出现问题,因为您必须检测更改并将新版本存储在磁盘上。
如果未显示身份验证对话框,并且您可以在浏览器中读取 WSDL,则您应该提供更多详细信息以检查其他可能的错误/问题。
这个问题绝对与服务本身无关,因为在向服务本身发出调用之前,SoapClient 已经在读取服务描述文档时卡住了。
编辑:
在本地拥有 WSDL 文件是第一步 - 这将使
SoapClient
了解如何与服务进行通信。 无论 WSDL 是直接从服务位置、其他服务器提供还是从本地文件读取,都没有关系 - 服务 URL 是在 WSDL 中编码的,因此SoapClient
始终知道在哪里查找服务端点。现在的第二个问题是
SoapClient
不支持 WS-Security 本机规范,这意味着您必须扩展SoapClient
来处理特定标头。 添加所需行为的扩展点为SoapClient::__doRequest( )
在将 XML 有效负载发送到服务端点之前对其进行预处理。 但我认为自己实现 WS-Security 解决方案需要对特定 WS-Security 规范有充分的了解。 也许还可以使用 < 创建 WS-Security 标头并将其打包到 XML 请求中code>SoapClient::__setSoapHeaders() 和相应的SoapHeader
,但我怀疑这是否可行,而将自定义SoapClient
扩展作为唯一的可能性。一个简单的
SoapClient
扩展将是对于基本的 WS-Security 身份验证,您必须将以下内容添加到 SOAP 标头:
但正如我上面所说: 我认为有关 WS-Security 的更多知识需要规范和给定的服务架构才能使其正常工作。
如果您需要适用于整个 WS-* 规范范围的企业级解决方案,并且您可以安装 PHP 模块,您应该查看 PHP 的 WSO2 Web 服务框架 (WSO2 WSF/PHP)
The problem seems to be that the WSDL document is somehow protected (basic authentication - I don't thinkg that digest authentication is supported with
SoapClient
, so you'd be out of luck in this case) and that theSoapClient
therefore cannot read and parse the service description.First of all you should try to open the WSDL location in your browser to check if you're presented an authentication dialog. If there is an authentication dialog you must make sure that the
SoapClient
uses the required login credentials on retrieving the WSDL document. The problem is thatSoapClient
will only send the credentials given with thelogin
andpassword
options (as well as thelocal_cert
option when using certificate authentication) on creating the client when invoking the service, not when fetching the WSDL (see here). There are two methods to overcome this problem:Add the login credentials to the WSDL url on the
SoapClient
constructor callThis should be the most simple solution - but in PHP Bug #27777 it is written that this won't work either (I haven't tried that).
ext/curl
or manually through your browser or viawget
for example, store it on disk and instantiate theSoapClient
with a reference to the local WSDL.This solution can be problematic if the WSDL document changes as you have to detect the change and store the new version on disk.
If no authentication dialog is shown and if you can read the WSDL in your browser, you should provide some more details to check for other possible errors/problems.
This problem is definitively not related to the service itself as
SoapClient
chokes already on reading the service descripion document before issuing a call to the service itself.EDIT:
Having the WSDL file locally is a first step - this will allow the
SoapClient
to know how to communicate with the service. It doesn't matter if the WSDL is directly served from the service location, from another server or is read from a local file - service urls are coded within the WSDL soSoapClient
always knows where to look for the service endpoint.The second problem now is that
SoapClient
has no support for the WS-Security specifications natively, which means you must extendSoapClient
to handle the specific headers. An extension point to add the required behaviour would beSoapClient::__doRequest()
which pre-processes the XML payload before sending it to the service endpoint. But I think that implementing the WS-Security solution yourself will require a decent knowledge of the specific WS-Security specifications. Perhaps WS-Security headers can also be created and packed into the XML request by usingSoapClient::__setSoapHeaders()
and the appropriateSoapHeader
s but I doubt that this will work, leaving the customSoapClient
extension as the lone possibility.A simple
SoapClient
extension would beFor a basic WS-Security authentication you would have to add the following to the SOAP-header:
But as I said above: I think that much more knowledge about the WS-Security specification and the given service architecture is needed to get this working.
If you need an enterprise grade solution for the whole WS-* specification range and if you can install PHP modules you should have a look at the WSO2 Web Services Framework for PHP (WSO2 WSF/PHP)
对于密码摘要安全性,您可以使用以下方法:
要将其与 PHP SoapClient 一起使用,请使用以下方式:
For a password digest security, you can use the following:
To use it with PHP SoapClient, use this way :
我有比扩展现有的soapclient 库更简单的解决方案。
第 1 步:创建两个类来创建 WSSE 标头的结构
第 2 步:为用户名和密码创建 Soap 变量
第 3 步:为 Auth 类创建对象并传入 Soap var
第 4 步:从 Auth 类的对象中创建 SoapVar
第 5 步:为 Token 类创建对象
步骤6:从Token类的对象中创建SoapVar
步骤7:为“Security”节点创建SoapVar
步骤8:从安全性soapvar中创建标头对象步骤
9:创建Soap Client的对象
步骤10:为soapclient对象设置标头
步骤11:最终调用方法
I have more simple solution than extending the existing soapclient library.
Step1: Create two classes to create a structure for WSSE headers
Step2: Create Soap Variables for UserName and Password
Step3: Create Object for Auth Class and pass in soap var
Step4: Create SoapVar out of object of Auth class
Step5: Create object for Token Class
Step6: Create SoapVar out of object of Token class
Step7: Create SoapVar for 'Security' node
Step8: Create header object out of security soapvar
Step9: Create object of Soap Client
Step10: Set headers for soapclient object
Step 11: Final call to method
我采用了 Alain Tiemblo 的优秀解决方案,但我使用密码而不是摘要。
要调用它,请使用
I adopted Alain Tiemblo's excellent solution, but I use the password rather than a digest.
To call it, use
使用摘要密码进行 WS 安全保护。 该代码对我有用:
使用:
WS Secure with digest password. This code work for me:
Use:
来自 php 文档
From the php documentation