使用带客户端证书的SoapClient连接SOAP WebService失败排查
问题:PHP SoapClient调用带客户端证书的本地SOAP服务失败(Could not connect to host)
搭建了本地SOAP服务器,使用客户端证书进行连接验证。通过SoapUI调用服务时可正常连接,但使用PHP的SoapClient却无法成功。尝试用file_get_contents带着相同的流上下文能正常获取WSDL,但将该上下文传入SoapClient构造函数时,会抛出SoapFault: Could not connect to host异常。
代码示例
<?php $context = stream_context_create( [ 'ssl' => [ 'local_cert' => __DIR__ . '/client.crt', 'local_pk' => __DIR__ . '/client.key', 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => false, ] ] ); $url = 'https://localhost:4430?WSDL'; echo "From file_get_contents: ".(file_get_contents($url, false, $context)).PHP_EOL; echo "== Making the call ==".PHP_EOL; try { $client = new SoapClient($url, [ 'stream_context' => $context, ]); echo "From ws call: ".$argv[1] . ' + ' . $argv[2] . ' = ' . $client->Add([ 'intA' => $argv[1], 'intB' => $argv[2], ])->AddResult; } catch (SoapFault $soapFault) { echo $soapFault->getMessage(); } echo PHP_EOL;
运行输出
From file_get_contents: <?xml version="1.0" encoding="UTF-8"?> <definitions name="Calculator" xmlns = "http://schemas.xmlsoap.org/wsdl/" targetNamespace="urn:Calculator" xmlns:tns="urn:Calculator" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"> <message name="addRequest"> <part name="a" type="xsd:int"/> <part name="b" type="xsd:int"/> </message> <message name="addResponse"> <part name="result" type="xsd:int"/> </message> <portType name="AddPort"> <operation name="add"> <input message="tns:addRequest"/> <output message="tns:addResponse"/> </operation> </portType> <binding name="AddBinding" type="tns:AddPort"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="add"> <soap:operation soapAction="urn:CalculatorAction"/> <input> <soap:body use="encoded" namespace="urn:Calculator" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <soap:body use="encoded" namespace="urn:Calculator" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding> <service name="WSDLService"> <documentation>Returns a greeting string.</documentation> <port name="AddPort" binding="tns:AddBinding"> <soap:address location="https://localhost:4430/calculator_server.php?wsdl"/> </port> </service> </definitions> == Making the call == Could not connect to host
问题分析与解决方案
1. 修正SSL上下文的自签名证书配置
本地测试服务器大概率使用自签名证书,但当前上下文里allow_self_signed设为false,会导致SoapClient验证服务器证书失败。修改该配置为true:
$context = stream_context_create( [ 'ssl' => [ 'local_cert' => __DIR__ . '/client.crt', 'local_pk' => __DIR__ . '/client.key', 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true, // 改为true ] ] );
2. 确保客户端证书与密钥的兼容性
部分PHP环境对分开的.crt和.key文件支持不佳,建议将两者合并为一个PEM格式文件:
# 合并证书和密钥到一个文件 cat client.crt client.key > client.pem
然后修改上下文配置,只保留local_cert指向合并后的文件:
'ssl' => [ 'local_cert' => __DIR__ . '/client.pem', 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true, ]
如果你的密钥设置了密码,还需要在SSL上下文里添加'passphrase' => '你的密钥密码'。
3. 调试SoapClient请求细节
添加trace选项可以获取请求的详细信息,帮助定位具体错误:
try { $client = new SoapClient($url, [ 'stream_context' => $context, 'trace' => true, // 开启请求追踪 'exceptions' => true, ]); // ... 调用代码 } catch (SoapFault $soapFault) { echo $soapFault->getMessage() . PHP_EOL; // 打印最后一次请求的头部和内容 echo "Request Headers:\n" . $client->__getLastRequestHeaders() . "\n"; echo "Request Content:\n" . $client->__getLastRequest() . "\n"; // 同时查看PHP错误日志,里面会有更详细的SSL连接失败原因 }
4. 验证WSDL中的服务地址
WSDL里的<soap:address location="https://localhost:4430/calculator_server.php?wsdl"/>是SoapClient实际调用的地址,需要确认该路径同样接受客户端证书验证,且可以正常访问。
内容的提问来源于stack exchange,提问作者Muc
相关产品推荐
相关产品推荐

