You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 16:10:28