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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/953639/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 00:27:12  来源:igfitidea点击:

Connecting to WS-Security protected Web Service with PHP

phpweb-servicessoapws-security

提问by Anthony

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:

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

$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

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

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

$server= new SoapServer("https://example.com/WSDL/nameofservice");

I get:

我得到:

<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>

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.

我还没有尝试发送原始请求信封以查看服务器返回的内容,但这可能是一种解决方法。但我希望有人能告诉我如何使用 php 内置类来设置它。我尝试将“用户名”和“密码”添加到数组中,但这并不好。问题是我什至不知道我是否到达了远程站点,更不用说它是否拒绝了请求。

采纳答案by Stefan Gehrig

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

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

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 SoapClientuses the required login credentials on retrieving the WSDL document. The problem is that SoapClientwill only send the credentials given with the loginand passwordoptions (as well as the local_certoption 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:

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

  1. Add the login credentials to the WSDL url on the SoapClientconstructor call

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

    This should be the most simple solution - but in PHP Bug #27777it is written that this won't work either (I haven't tried that).

  2. Fetch the WSDL manually using the HTTP stream wrapper or ext/curlor manually through your browser or via wgetfor example, store it on disk and instantiate the SoapClientwith a reference to the local 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.

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

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

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

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

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

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

This problem is definitively not related to the service itself as SoapClientchokes already on reading the service descripion document before issuing a call to the service itself.

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

EDIT:

编辑:

Having the WSDL file locally is a first step - this will allow the SoapClientto 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 SoapClientalways knows where to look for the service endpoint.

在本地拥有 WSDL 文件是第一步 - 这将允许SoapClient了解如何与服务进行通信。WSDL 是直接从服务位置、从另一台服务器还是从本地文件读取都无关紧要 - 服务 URL 编码在 WSDL 中,因此SoapClient始终知道在哪里查找服务端点。

The second problem now is that SoapClienthas no support for the WS-Securityspecifications natively, which means you must extend SoapClientto 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 SoapClientextension as the lone possibility.

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

A simple SoapClientextension would be

一个简单的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);
    }
}

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

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

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

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-Security 规范和给定服务架构的知识才能使其正常工作。

If you need an enterprise grade solution for the whole WS-* specification range and if you can install PHP modules you should have a look at the WSO2 Web Services Framework for PHP (WSO2 WSF/PHP)

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

回答by Chris

Simply extend the SoapHeader to create a Wsse compilant authentication:

只需扩展 SoapHeader 即可创建 Wsse 兼容身份验证:

class WsseAuthHeader extends SoapHeader {

private $wss_ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-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);
}
}



$wsse_header = new WsseAuthHeader($username, $password);
$x = new SoapClient('{...}', array("trace" => 1, "exception" => 0));
$x->__setSoapHeaders(array($wsse_header));


If you need to use ws-security with a nonce and a timestamp, Peter has posted an update version on http://php.net/manual/en/soapclient.soapclient.php#114976of which he wrote that it did work for him:

如果您需要使用带有 nonce 和时间戳的 ws-security,Peter 在http://php.net/manual/en/soapclient.soapclient.php#114976上发布了一个更新版本,他写道它确实适用于他:

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)
    {
        $created    = gmdate('Y-m-d\TH:i:s\Z');
        $nonce      = mt_rand();
        $passdigest = base64_encode(pack('H*', sha1(pack('H*', $nonce) . pack('a*', $created) . pack('a*', $pass))));

        $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);
        $auth->Nonce    = new SoapVar($passdigest, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);
        $auth->Created  = new SoapVar($created, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wsu_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);
    }
}

compare as well with the details given in answer https://stackoverflow.com/a/18575154/367456

与答案https://stackoverflow.com/a/18575154/367456中给出的细节进行比较

回答by Alain Tiemblo

For a password digest security, you can use the following:

对于密码摘要安全性,您可以使用以下内容:

   /**
    * This function implements a WS-Security digest authentification for PHP.
    *
    * @access private
    * @param string $user
    * @param string $password
    * @return SoapHeader
    */
   function soapClientWSSecurityHeader($user, $password)
   {
      // Creating date using yyyy-mm-ddThh:mm:ssZ format
      $tm_created = gmdate('Y-m-d\TH:i:s\Z');
      $tm_expires = gmdate('Y-m-d\TH:i:s\Z', gmdate('U') + 180); //only necessary if using the timestamp element

      // Generating and encoding a random number
      $simple_nonce = mt_rand();
      $encoded_nonce = base64_encode($simple_nonce);

      // Compiling WSS string
      $passdigest = base64_encode(sha1($simple_nonce . $tm_created . $password, true));

      // Initializing namespaces
      $ns_wsse = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
      $ns_wsu = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
      $password_type = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest';
      $encoding_type = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary';

      // Creating WSS identification header using SimpleXML
      $root = new SimpleXMLElement('<root/>');

      $security = $root->addChild('wsse:Security', null, $ns_wsse);

      //the timestamp element is not required by all servers
      $timestamp = $security->addChild('wsu:Timestamp', null, $ns_wsu);
      $timestamp->addAttribute('wsu:Id', 'Timestamp-28');
      $timestamp->addChild('wsu:Created', $tm_created, $ns_wsu);
      $timestamp->addChild('wsu:Expires', $tm_expires, $ns_wsu);

      $usernameToken = $security->addChild('wsse:UsernameToken', null, $ns_wsse);
      $usernameToken->addChild('wsse:Username', $user, $ns_wsse);
      $usernameToken->addChild('wsse:Password', $passdigest, $ns_wsse)->addAttribute('Type', $password_type);
      $usernameToken->addChild('wsse:Nonce', $encoded_nonce, $ns_wsse)->addAttribute('EncodingType', $encoding_type);
      $usernameToken->addChild('wsu:Created', $tm_created, $ns_wsu);

      // Recovering XML value from that object
      $root->registerXPathNamespace('wsse', $ns_wsse);
      $full = $root->xpath('/root/wsse:Security');
      $auth = $full[0]->asXML();

      return new SoapHeader($ns_wsse, 'Security', new SoapVar($auth, XSD_ANYXML), true);
   }

To use it with PHP SoapClient, use this way :

要将它与 PHP SoapClient 一起使用,请使用以下方式:

$client = new SoapClient('http://endpoint');
$client->__setSoapHeaders(soapClientWSSecurityHeader('myUser', 'myPassword'));
// $client->myService(array('param' => 'value', ...);

回答by Bhargav Khatana

I have more simple solution than extending the existing soapclient library.

我有比扩展现有的soapclient库更简单的解决方案。

Step1: Create two classes to create a structure for WSSE headers

步骤 1:创建两个类来为 WSSE 标头创建结构

class clsWSSEAuth {
    private $Username;
    private $Password;
    function __construct($username, $password) {
        $this->Username=$username;
        $this->Password=$password;
    }
}

class clsWSSEToken {
    private $UsernameToken;
    function __construct ($innerVal){
        $this->UsernameToken = $innerVal;
    }
}

Step2: Create Soap Variables for UserName and Password

步骤 2:为用户名和密码创建 Soap 变量

$username = 1111;
$password = 1111;

//Check with your provider which security name-space they are using.
$strWSSENS = "http://schemas.xmlsoap.org/ws/2002/07/secext";

$objSoapVarUser = new SoapVar($username, XSD_STRING, NULL, $strWSSENS, NULL, $strWSSENS);
$objSoapVarPass = new SoapVar($password, XSD_STRING, NULL, $strWSSENS, NULL, $strWSSENS);

Step3: Create Object for Auth Class and pass in soap var

Step3:为Auth类创建对象并传入soap var

$objWSSEAuth = new clsWSSEAuth($objSoapVarUser, $objSoapVarPass);

Step4: Create SoapVar out of object of Auth class

Step4:从Auth类的对象中创建SoapVar

$objSoapVarWSSEAuth = new SoapVar($objWSSEAuth, SOAP_ENC_OBJECT, NULL, $strWSSENS, 'UsernameToken', $strWSSENS);

Step5: Create object for Token Class

Step5:为Token类创建对象

$objWSSEToken = new clsWSSEToken($objSoapVarWSSEAuth);

Step6: Create SoapVar out of object of Token class

Step6:从Token类的对象中创建SoapVar

$objSoapVarWSSEToken = new SoapVar($objWSSEToken, SOAP_ENC_OBJECT, NULL, $strWSSENS, 'UsernameToken', $strWSSENS);

Step7: Create SoapVar for 'Security' node

步骤 7:为“安全”节点创建 SoapVar

$objSoapVarHeaderVal=new SoapVar($objSoapVarWSSEToken, SOAP_ENC_OBJECT, NULL, $strWSSENS, 'Security', $strWSSENS);

Step8: Create header object out of security soapvar

Step8:从安全soapvar创建头对象

$objSoapVarWSSEHeader = new SoapHeader($strWSSENS, 'Security', $objSoapVarHeaderVal,true, 'http://abce.com');

//Third parameter here makes 'mustUnderstand=1
//Forth parameter generates 'actor="http://abce.com"'

Step9: Create object of Soap Client

Step9:创建Soap Client对象

$objClient = new SoapClient($WSDL, $arrOptions);

Step10: Set headers for soapclient object

Step10:设置soapclient对象的headers

$objClient->__setSoapHeaders(array($objSoapVarWSSEHeader));

Step 11: Final call to method

第 11 步:最终调用方法

$objResponse = $objClient->__soapCall($strMethod, $requestPayloadString);

回答by Scott C Wilson

I adopted Alain Tiemblo's excellent solution, but I use the password rather than a digest.

我采用了 Alain Tiemblo 的优秀解决方案,但我使用的是密码而不是摘要。

    /**
    * This function implements a WS-Security authentication for PHP.
    *
    * @access private
    * @param string $user
    * @param string $password
    * @return SoapHeader
    */
    function soapClientWSSecurityHeader($user, $password)
   {
      // Creating date using yyyy-mm-ddThh:mm:ssZ format
      $tm_created = gmdate('Y-m-d\TH:i:s\Z');
      $tm_expires = gmdate('Y-m-d\TH:i:s\Z', gmdate('U') + 180); //only necessary if using the timestamp element

      // Generating and encoding a random number
      $simple_nonce = mt_rand();
      $encoded_nonce = base64_encode($simple_nonce);

      // Compiling WSS string
      $passdigest = base64_encode(sha1($simple_nonce . $tm_created . $password, true));

      // Initializing namespaces
      $ns_wsse = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
      $ns_wsu = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
      $password_type = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText';
      $encoding_type = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary';

      // Creating WSS identification header using SimpleXML
      $root = new SimpleXMLElement('<root/>');

      $security = $root->addChild('wsse:Security', null, $ns_wsse);

      //the timestamp element is not required by all servers
      $timestamp = $security->addChild('wsu:Timestamp', null, $ns_wsu);
      $timestamp->addAttribute('wsu:Id', 'Timestamp-28');
      $timestamp->addChild('wsu:Created', $tm_created, $ns_wsu);
      $timestamp->addChild('wsu:Expires', $tm_expires, $ns_wsu);

      $usernameToken = $security->addChild('wsse:UsernameToken', null, $ns_wsse);
      $usernameToken->addChild('wsse:Username', $user, $ns_wsse);
      $usernameToken->addChild('wsse:Password', $password, $ns_wsse)->addAttribute('Type', $password_type);
      $usernameToken->addChild('wsse:Nonce', $encoded_nonce, $ns_wsse)->addAttribute('EncodingType', $encoding_type);
      $usernameToken->addChild('wsu:Created', $tm_created, $ns_wsu);

      // Recovering XML value from that object
      $root->registerXPathNamespace('wsse', $ns_wsse);
      $full = $root->xpath('/root/wsse:Security');
      $auth = $full[0]->asXML();

      return new SoapHeader($ns_wsse, 'Security', new SoapVar($auth, XSD_ANYXML), true);
   }

To call it, use

要调用它,请使用

$client = new SoapClient('YOUR ENDPOINT');
$userid = "userid";
$password = "password"; 
$client->__setSoapHeaders(soapClientWSSecurityHeader($userid,$password));

回答by Robert Haliullov

WS Secure with digest password. This code work for me:

WS Secure 使用摘要密码。这段代码对我有用:

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';
    private $type_password_digest= 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest';
    private $type_password_text= 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText';
    private $encoding_type_base64 = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary';

    private function authText($user, $pass) {
        $auth = new stdClass();
        $auth->Username = new SoapVar($user, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);
        $auth->Password = new SoapVar('<ns2:Password Type="'.$this->type_password_text.'">' . $pass . '</ns2:Password>', XSD_ANYXML );
        return $auth;
    }

    private function authDigest($user, $pass) {
        $created = gmdate('Y-m-d\TH:i:s\Z');
        $nonce = mt_rand();
        $enpass = base64_encode(pack('H*', sha1(pack('H*', $nonce) . pack('a*', $created) . pack('a*', $pass))));
        $auth = new stdClass();
        $auth->Username = new SoapVar($user, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);
        $auth->Password = new SoapVar('<ns2:Password Type="'.$this->type_password_digest.'">' . $enpass . '</ns2:Password>', XSD_ANYXML );
        $auth->Nonce = new SoapVar('<ns2:Nonce EncodingType="' . $this->encoding_type_base64 . '">' . base64_encode(pack('H*', $nonce)) . '</ns2:Nonce>', XSD_ANYXML);
        $auth->Created = new SoapVar($created, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wsu_ns);
        return $auth;
    }

    function __construct($user, $pass, $useDigest=true) {
        if ($useDigest) {
            $auth = $this->authDigest($user, $pass);
        }else{
            $auth = $this->authText($user, $pass);
        }
        $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);
    }
}

Use:

用:

 $client->__setSoapHeaders([new WsseAuthHeader($login, $password)]);

回答by Jase Whatson

$client = new SoapClient("some.wsdl", array('login'    => "some_name",
                                            'password' => "some_password"));

From the php documentation

来自 php 文档