如何在PHP Web Service请求中设置传输安全以解决消息验证错误
PHP调用Web Service安全验证报错解决方案
问题描述
调用Web Service时触发错误:"An error occurred when verifying security for the message"。已知.NET中通过设置TransportWithMessageCredential安全模式解决过类似问题,但不清楚PHP中的实现方式。
现有PHP代码
<?php date_default_timezone_set('Europe/London'); class WsseAuthHeader extends SoapHeader { private $wss_ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wsswssecurity-secext-1.0.xsd'; function __construct($user, $pass, $ns = null) { if($ns) { $this->wss_ns = $ns; } $auth = new stdClass(); $auth->Username = new SoapVar($user, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns); $auth->Password = new SoapVar($pass, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns); $username_token = new stdClass(); $username_token->UsernameToken = new SoapVar($auth, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns); $security_sv = new SoapVar( new SoapVar($username_token, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns), SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'Security', $this->wss_ns ); parent::__construct($this->wss_ns, 'Security', $security_sv, true); } } $username = "REDACTED"; $password = "REDACTED"; $wsse_header = new WsseAuthHeader($username, $password); $options = array( 'soap_version' => SOAP_1_1, 'exceptions' => true, 'trace' => 1, 'wdsl_local_copy' => true ); // This is currently linked to the pilot or test site. $wsdl = 'REDACTED'; $soapClient = new SoapClient($wsdl, $options); $soapClient->__setSoapHeaders(array($wsse_header)); $objParam = new stdClass(); $objParam->ProfileIDVersion = new stdClass(); $objParam->ProfileIDVersion->ID = '0c5f1b29-e145-4dce-a058-0ebccbaa9ba4'; // This can be found via the admin portal of id3 global. $objParam->ProfileIDVersion->Version = 0; //Setting this to zero will by default call the latest active version of the profile $objParam->CustomerReference = "particular customer"; $objParam->InputData = new stdClass(); $objParam->InputData->Personal = new stdClass(); $objParam->InputData->Personal->PersonalDetails = new stdClass(); //$objParam->InputData->Personal->PersonalDetails->Title='Mr'; $objParam->InputData->Personal->PersonalDetails->Forename='Ruth'; $objParam->InputData->Personal->PersonalDetails->MiddleName=''; $objParam->InputData->Personal->PersonalDetails->Surname='Goodwin'; $objParam->InputData->Personal->PersonalDetails->Gender='Female'; $objParam->InputData->Personal->PersonalDetails->DOBDay='01'; $objParam->InputData->Personal->PersonalDetails->DOBMonth='12'; $objParam->InputData->Personal->PersonalDetails->DOBYear='1976'; $objParam->InputData->Addresses = new stdClass(); $objParam->InputData->Addresses->CurrentAddress = new stdClass(); $objParam->InputData->Addresses->CurrentAddress->Country='United Kingdom'; $objParam->InputData->Addresses->CurrentAddress->Street='High Street'; $objParam->InputData->Addresses->CurrentAddress->City='Westbury'; $objParam->InputData->Addresses->CurrentAddress->ZipPostcode='BA133BN'; $objParam->InputData->Addresses->CurrentAddress->Building='387'; $objParam->InputData->Addresses->CurrentAddress->SubBuilding=''; if (is_soap_fault($soapClient)) { echo "A"; throw new Exception(" {$soapClient->faultcode}: {$soapClient->faultstring} "); } $objRet = null; try { $objRet = $soapClient->AuthenticateSP($objParam); echo '<pre>'; print"Decision Band :".($objRet->AuthenticateSPResult->BandText)."<br>"; echo '</pre>'; } catch (Exception $e) { //echo "B"; echo "<pre>"; print_r($e); echo "</pre>"; } if (is_soap_fault($objRet)) { echo "C"; throw new Exception(" {$objRet->faultcode}: {$objRet->faultstring} "); } ?>
可用的C#绑定配置
<binding name="wsHttpBinding_GlobalAuthenticate"> <security mode="TransportWithMessageCredential"> <transport clientCredentialType="None" /> <message clientCredentialType="UserName" /> </security> </binding>
解决方案
你的C#配置指定了TransportWithMessageCredential模式:传输层依赖HTTPS加密,消息层通过用户名密码做身份验证。PHP端需要对应调整以下几点:
1. 确认WSDL地址为HTTPS
确保$wsdl指向的是HTTPS地址,符合Transport安全要求。
2. 切换SOAP版本为1.2
wsHttpBinding默认使用SOAP 1.2,原代码用的是SOAP 1.1,需要修改:
$options = array( 'soap_version' => SOAP_1_2, // 从SOAP_1_1改为SOAP_1_2 'exceptions' => true, 'trace' => 1, 'wdsl_local_copy' => true );
3. 修复WSSE认证头结构
原WsseAuthHeader缺少WSS规范要求的Nonce(随机数)和Created(时间戳)元素,且密码类型未明确指定。修改后的认证头类如下:
class WsseAuthHeader extends SoapHeader { private $wss_ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'; private $wsu_ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'; function __construct($user, $pass) { // 生成随机Nonce和当前UTC时间戳 $nonce = base64_encode(random_bytes(16)); $created = gmdate('Y-m-d\TH:i:s\Z'); $auth = new stdClass(); $auth->Username = new SoapVar($user, XSD_STRING, null, $this->wss_ns, 'Username', $this->wss_ns); // 指定密码类型为PasswordText(对应C#的UserName凭证) $auth->Password = new SoapVar( $pass, XSD_STRING, null, $this->wss_ns, 'Password', $this->wss_ns, null, array('Type' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText') ); $auth->Nonce = new SoapVar( $nonce, XSD_STRING, null, $this->wss_ns, 'Nonce', $this->wss_ns ); $auth->Created = new SoapVar( $created, XSD_STRING, null, $this->wsu_ns, 'Created', $this->wsu_ns ); $usernameToken = new SoapVar( $auth, SOAP_ENC_OBJECT, null, $this->wss_ns, 'UsernameToken', $this->wss_ns ); $security = new SoapVar( $usernameToken, SOAP_ENC_OBJECT, null, $this->wss_ns, 'Security', $this->wss_ns ); parent::__construct($this->wss_ns, 'Security', $security, true); } }
4. 验证请求结构(可选)
开启trace=1后,可以在catch块中打印请求内容,确认头信息是否符合要求:
catch (Exception $e) { echo "<pre>"; print_r($e); // 打印SOAP请求内容 echo "Request:\n" . htmlspecialchars($soapClient->__getLastRequest()) . "\n"; echo "</pre>"; }
关键说明
TransportWithMessageCredential要求必须通过HTTPS访问服务,确保传输过程加密。- WSSE认证头必须包含
Username、Password、Nonce和Created元素,部分服务端会严格校验这些字段。 - 密码类型
PasswordText是明文传输,但因为有HTTPS加密,不会泄露敏感信息;如果服务端要求PasswordDigest,需要修改密码为base64(sha1(nonce + created + password))格式。
内容的提问来源于stack exchange,提问作者brummie49
相关产品推荐
相关产品推荐

