使用 PHP 连接到受 WS-Security 保护的 Web 服务 [英] Connecting to WS-Security protected Web Service with PHP

查看:24
本文介绍了使用 PHP 连接到受 WS-Security 保护的 Web 服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试连接到受密码保护且 URL 为 https 的 Web 服务.在脚本发出请求之前,我不知道如何进行身份验证.似乎我一定义服务就会发出请求.例如,如果我输入:

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

如果我尝试将服务定义为 Soap Server,例如:

If I try defining the service as a Soap Server, like:

$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 内置类来设置它.我尝试将用户名"和密码"添加到数组中,但这并不好.问题是我根本不知道我是否到达了远程站点,更不用说它是否拒绝了请求.

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.

推荐答案

问题似乎是 WSDL 文档以某种方式受到保护(基本身份验证 - 我不认为 SoapClient,所以在这种情况下你会不走运)并且 SoapClient 因此无法读取和解析服务描述.

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 the SoapClient therefore cannot read and parse the service description.

首先,您应该尝试在浏览器中打开 WSDL 位置,以检查是否出现了身份验证对话框.如果存在身份验证对话框,您必须确保 SoapClient 在检索 WSDL 文档时使用所需的登录凭据.问题是 SoapClient 只会发送通过 loginpassword 选项(以及 local_cert> 在调用服务时创建客户端时使用证书身份验证的选项,而不是在获取 WSDL 时(参见 此处).有两种方法可以解决这个问题:

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 that SoapClient will only send the credentials given with the login and password options (as well as the local_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:

  1. 将登录凭据添加到 SoapClient 构造函数调用上的 WSDL url

  1. Add the login credentials to the WSDL url on the SoapClient constructor call

$client = new SoapClient(
    'https://' . urlencode($login) . ':' . urlencode($password) . '@example.com/WSDL/nameofservice',
    array(
        'login' => $login,
        'password' => $password
    )
);

这应该是最简单的解决方案 - 但在 PHP 中 Bug #27777据说这也行不通(我没试过).

This 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).

使用 HTTP 流包装器或 ext/curl 手动获取 WSDL,或者通过浏览器或通过 wget 手动获取 WSDL,例如,将其存储在磁盘上并实例化SoapClient 引用本地 WSDL.

Fetch the WSDL manually using the HTTP stream wrapper or ext/curl or manually through your browser or via wgetfor example, store it on disk and instantiate the SoapClient with a reference to the local WSDL.

如果 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.

如果未显示身份验证对话框,并且您可以在浏览器中阅读 WSDL,则应提供更多详细信息以检查其他可能的错误/问题.

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.

这个问题肯定与服务本身无关,因为 SoapClient 在发出对服务本身的调用之前已在阅读服务描述文档时窒息.

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.

在本地拥有 WSDL 文件是第一步 - 这将使 SoapClient 知道如何与服务通信.WSDL 是直接从服务位置、另一台服务器还是从本地文件读取都没有关系 - 服务 URL 编码在 WSDL 中,因此 SoapClient 总是知道在哪里寻找服务端点.

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 so SoapClient always knows where to look for the service endpoint.

现在的第二个问题是 SoapClient 不支持 WS-Security 本机规范,这意味着您必须扩展 SoapClient 以处理特定的标头.添加所需行为的扩展点是 SoapClient::__doRequest() 在将 XML 负载发送到服务端点之前对其进行预处理.但是我认为自己实现 WS-Security 解决方案需要对特定的 WS-Security 规范有很好的了解.也许还可以使用 SoapClient::__setSoapHeaders() 和适当的SoapHeaders 但我怀疑这是否可行,将自定义 SoapClient 扩展作为唯一的可能性.

The second problem now is that SoapClient has no support for the WS-Security specifications natively, which means you must extend SoapClient to handle the specific headers. An extension point to add the required behaviour would be SoapClient::__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 using SoapClient::__setSoapHeaders() and the appropriate SoapHeaders but I doubt that this will work, leaving the custom SoapClient extension as the lone possibility.

一个简单的 SoapClient 扩展是

class My_SoapClient extends SoapClient
{
    protected function __doRequest($request, $location, $action, $version) 
    {
        /*
         * $request is a XML string representation of the SOAP request
         * that can e.g. be loaded into a DomDocument to make it modifiable.
         */
        $domRequest = new DOMDocument();
        $domRequest->loadXML($request);

        // modify XML using the DOM API, e.g. get the <s:Header>-tag 
        // and add your custom headers
        $xp = new DOMXPath($domRequest);
        $xp->registerNamespace('s', 'http://www.w3.org/2003/05/soap-envelope');
        // fails if no <s:Header> is found - error checking needed
        $header = $xp->query('/s:Envelope/s:Header')->item(0);

        // now add your custom header
        $usernameToken = $domRequest->createElementNS('http://schemas.xmlsoap.org/ws/2002/07/secext', 'wsse:UsernameToken');
        $username = $domRequest->createElementNS('http://schemas.xmlsoap.org/ws/2002/07/secext', 'wsse:Username', 'userid');
        $password = $domRequest->createElementNS('http://schemas.xmlsoap.org/ws/2002/07/secext', 'wsse:Password', 'password');
        $usernameToken->appendChild($username);
        $usernameToken->appendChild($password);
        $header->appendChild($usernameToken);

        $request = $domRequest->saveXML();
        return parent::__doRequest($request, $location, $action, $version);
    }
}

对于基本的 WS-Security 身份验证,您必须将以下内容添加到 SOAP 标头中:

For a basic WS-Security authentication you would have to add the following to the SOAP-header:

<wsse:UsernameToken>
    <wsse:Username>userid</wsse:Username>
    <wsse:Password>password</wsse:Password>                                 
</wsse:UsernameToken>

但正如我上面所说:我认为需要更多关于 WS-Security 规范和给定服务架构的知识才能使其正常工作.

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.

如果您需要适用于整个 WS-* 规范范围的企业级解决方案,并且如果您可以安装 PHP 模块,您应该查看 PHP 的 WSO2 Web 服务框架 (WSO2 WSF/PHP)

这篇关于使用 PHP 连接到受 WS-Security 保护的 Web 服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆