当前位置: 首页 > 面试题库 >

使用PHP连接到受WS-Security保护的Web服务

乐健
2023-03-14
问题内容

我正在尝试连接到受密码保护且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,例如:

$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”添加到数组,但这不好。问题是我什至无法告诉我是否要到达远程站点,更不用说它是否拒绝了请求。


问题答案:

问题似乎是WSDL文档受到某种方式的保护(基本身份验证-
我不认为摘要身份验证受支持SoapClient,因此在这种情况下您会很不走运),因此WSDL文档SoapClient无法读取和解析服务描述。

首先,您应该尝试在浏览器中打开WSDL位置,以检查是否显示了身份验证对话框。如果存在认证对话框,则必须确保SoapClientWSDL在检索WSDL文档时使用必需的登录凭证。问题是,SoapClient将只发送给定的凭证loginpassword(以及选项local_cert使用证书认证时选择)上调用该服务时,获取的WSDL不是当创建客户端。有两种方法可以解决此问题:

  1. SoapClient构造函数调用中将登录凭证添加到WSDL URL
    $client = new SoapClient(
    'https://' . urlencode($login) . ':' . urlencode($password) . '@example.com/WSDL/nameofservice',
    array(
        'login' => $login,
        'password' => $password
    )
    

    );

这应该是最简单的解决方案-但是在PHP Bug#27777中,它写成也不起作用(我没有尝试过)。

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

如果WSDL文档发生更改(因为您必须检测到更改并将新版本存储在磁盘上),则此解决方案可能会出现问题。

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

最终,此问题与服务本身无关,因为SoapClient在发出对服务本身的调用之前读取服务描述文档时已经遇到了麻烦。

编辑:

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

现在的第二个问题是本机SoapClient不支持WS-Security规范,这意味着您必须扩展SoapClient以处理特定的标头。添加所需行为的扩展点是在将SoapClient::__doRequest()XML有效载荷发送到服务端点之前对其进行预处理。但我认为,自己实施WS-
Security解决方案将需要对特定WS-Security规范有相当的了解。也许还可以使用SoapClient::__setSoapHeaders()和适当SoapHeader的来创建WS-Security标头并将其打包到XML请求中,但我怀疑这样做是否可行,将定制SoapClient扩展名留为唯一可能性。

一个简单的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标头中:

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

但是正如我上面所说:我认为需要更多有关WS-Security规范和给定服务体系结构的知识才能使此工作正常进行。

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



 类似资料:
  • 问题内容: 我正在使用Spring Security在Grails应用程序中托管REST Web服务,即: 我是从Android应用程式呼叫它。(调用不安全的服务就可以了。) 为了调用该服务,我正在手动建立一个request()并使用来执行它。 我想知道最佳实践是什么,以及如何实施它们……具体来说,我应该: 执行一次登录,以检索JSESSION_ID,然后将包含它的标头添加到每个后续请求的Http

  • 我正在寻找一个最佳实践和高效的解决方案,以确保通过REST与Web客户端应用程序通信的多个微服务的安全。 当前设置: 这些微服务是用Java制作的,带有Spring框架,并运行在Docker容器中。 客户端是一个Angular 2应用程序。 我创建了一个新的µ服务,它将充当“网关”,是我的web客户端和其他服务之间的唯一通信点。 我从远程身份验证API检索JWT加密令牌(让我们称之为LOCK) 我

  • 如果当前请求是安全的,有没有办法“询问”Spring Security性?因为即使我通过了身份验证,我也要检测我是在受安全保护的URL中还是在匿名/公共页面中 提前感谢!

  • 问题内容: 为什么定义中的所有方法都是隐式的?为什么不允许使用方法? 问题答案: 因为接口应该表示“您可以在课堂外看到的东西”。添加非公共方法是没有意义的。

  • 有没有办法只使用spring security实现CSRF保护,而不使用身份验证和授权等其他功能? 我尝试了以下配置,但它关闭了spring security的所有功能。想知道是否有一种方法可以配置csrf功能。

  • 问题内容: 该问题在某种程度上与以下链接的问题有关。但是,我需要在某些方面和一些其他信息上多一点清晰度。 我需要使用令牌为REST Web服务实现安全性 该Web服务旨在与Java客户端一起使用。因此,表单身份验证和凭据弹出窗口没有用。 我是REST安全和加密的新手 这是我到目前为止所了解的: 对于第一个请求: 用户建立https连接(或容器使用301确保https) 用户POST输入用户名和密码