Intereting Posts
Как преобразовать вывод number_format обратно в числа в PHP? отправленная почта была удалена на моем компьютере, но не удалось использовать сервер с помощью phpmailer Вывод буфера вывода командной строки PHP независимо от настроек буфера MongoDB Indexing vs Array Implementation для нашего конкретного приложения Получение первого изображения в строке с php Как выделить изменения / различия в одном текстовом абзаце другого? Как разобрать stdClass из Amazon API Response для возврата строки Что означает двоеточие перед литералом в выражении SQL? PHP усекает данные MSSQL Blob (4096b), даже после установки значений INI. Я пропустил один? PHP сгенерированная форма не отправляет AJAX PHP – Инициализация элементов объекта с параметром массива как получить дату от Excel с помощью библиотеки PHPExcel Создание файла .pem из pushnotification с PHP под LINUX w Пользовательский модуль Magento Как сохранить переменную в файле config.xml Zend Framework, как настроить заголовки

Подключение к защищенной веб-службе WS-Security с помощью PHP

Я пытаюсь подключиться к веб-службе, которая защищена паролем, а URL-адрес – https. Я не могу понять, как выполнить аутентификацию, прежде чем скрипт сделает запрос. Похоже, он делает запрос, как только я определяю услугу. Например, если я добавлю:

$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 , поэтому в этом случае вам не повезло), и поэтому SoapClient не может читать и анализировать описание услуги.

    Прежде всего, вы должны попытаться открыть местоположение WSDL в своем браузере, чтобы проверить, представлено ли вам диалоговое окно проверки подлинности. Если есть диалог аутентификации, вы должны убедиться, что SoapClient использует необходимые учетные данные для входа в документ WSDL. Проблема заключается в том, что SoapClient будет отправлять учетные данные, заданные параметрами login и password (а также параметр local_cert при использовании проверки подлинности сертификата) при создании клиента при вызове службы, а не при извлечении WSDL (см. Здесь ). Существует два способа преодоления этой проблемы:

    1. Добавьте учетные данные для входа в URL-адрес WSDL в SoapClient конструктора SoapClient

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

      Это должно быть самым простым решением, но в PHP Bug # 27777 написано, что это тоже не сработает (я этого не пробовал).

    2. Извлеките WSDL вручную, используя обертку HTTP-потока или ext/curl или вручную через браузер или через wget например, сохраните его на диске и SoapClient экземпляр SoapClient со ссылкой на локальный WSDL.

      Это решение может быть проблематичным, если документ WSDL изменяется, поскольку вам необходимо обнаружить изменение и сохранить новую версию на диске.

    Если диалоговое окно проверки подлинности не отображается, и если вы можете прочитать WSDL в своем браузере, вы должны предоставить дополнительную информацию, чтобы проверить другие возможные ошибки / проблемы.

    Эта проблема окончательно не связана с самой службой, поскольку SoapClient задыхается уже при чтении документа описания службы перед тем, как выступить с вызовом самой службы.

    РЕДАКТИРОВАТЬ:

    Наличие локального WSDL-файла – это первый шаг – это позволит SoapClient узнать, как связаться с сервисом. Не имеет значения, напрямую ли WSDL обслуживается из местоположения службы, с другого сервера или считывается из локального файла. Сервисные URL кодируются в WSDL, поэтому SoapClient всегда знает, где искать конечную точку службы.

    Вторая проблема заключается в том, что SoapClient не поддерживает спецификации WS-Security изначально, а это значит, что вы должны расширять SoapClient для обработки определенных заголовков. Точкой расширения для добавления требуемого поведения будет SoapClient::__doRequest() который предварительно обрабатывает полезную нагрузку XML перед отправкой в ​​конечную точку службы. Но я думаю, что для реализации самого решения WS-Security потребуется достойное знание конкретных спецификаций WS-Security. Возможно, заголовки WS-Security также могут быть созданы и упакованы в XML-запрос с помощью SoapClient::__setSoapHeaders() и соответствующего SoapHeader но я сомневаюсь, что это сработает, оставив пользовательское расширение 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 eg be loaded into a DomDocument to make it modifiable. */ $domRequest = new DOMDocument(); $domRequest->loadXML($request); // modify XML using the DOM API, eg 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, вам следует взглянуть на WSO2 Web Services Framework для PHP (WSO2 WSF / PHP)

    Просто добавьте 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)); 

    Если вам нужно использовать ws-security с помощью nonce и timestamp, 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('Ymd\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); } } 

    сравните также с данными, приведенными в ответе https://stackoverflow.com/a/18575154/367456

    Для обеспечения безопасности паролей вы можете использовать следующее:

      /** * 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('Ymd\TH:i:s\Z'); $tm_expires = gmdate('Ymd\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); } 

    Чтобы использовать его с PHP SoapClient, используйте этот способ:

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

    У меня есть более простое решение, чем расширение существующей библиотеки soapclient.

    Шаг 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; } } 

    Шаг 2. Создайте переменные мыла для имени пользователя и пароля.

     $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); 

    Шаг 3. Создайте объект для класса Auth и перейдите в мыло var

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

    Шаг 4: Создайте SoapVar из объекта класса Auth

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

    Шаг 5. Создание объекта для класса Token

     $objWSSEToken = new clsWSSEToken($objSoapVarWSSEAuth); 

    Шаг 6: Создайте SoapVar из объекта класса Token

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

    Шаг 7: Создайте SoapVar для узла «Безопасность»

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

    Шаг 8: Создайте объект заголовка вне безопасности soapvar

     $objSoapVarWSSEHeader = new SoapHeader($strWSSENS, 'Security', $objSoapVarHeaderVal,true, 'http://abce.com'); //Third parameter here makes 'mustUnderstand=1 //Forth parameter generates 'actor="http://abce.com"' 

    Шаг 9: Создайте объект клиента Soap

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

    Шаг 10: Установите заголовки для объекта soapclient

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

    Шаг 11: Заключительный вызов метода

     $objResponse = $objClient->__soapCall($strMethod, $requestPayloadString); 
     $client = new SoapClient("some.wsdl", array('login' => "some_name", 'password' => "some_password")); 

    Из документации php

    Я принял отличное решение Алена Тимбло, но я использую пароль, а не дайджест.

      /** * 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('Ymd\TH:i:s\Z'); $tm_expires = gmdate('Ymd\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); } 

    Чтобы позвонить, используйте

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

    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('Ymd\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); } } 

    Использование:

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