Авторизация по HTTP перед SOAP

Dzen

Новичок
Добрый день,

Когда-то давно писал код для работы по API через SOAP.

PHP:
$wsdl = 'https://...URL....';
ini_set("soap.wsdl_cache_enabled", "0");
# SOAP object
$client = new SoapClient($wsdl,
    array(
        'encoding' => 'UTF-8',
    )
);

# Login and OAuth token
$token = '...........';

# SOAP headers
$client->__setSoapHeaders(
   array(
      new SoapHeader('API', 'token', $token, false)
   )
);

$result = $client->.......
Авторизация по Токену работала передаваясь в заголовке SOAP запроса.
Всё было ок.

Тех.поддержка API , поменяла принцип авторизации, и теперь просит указывать токен в HTTP-заголовке, а не в SOAP запросе.

То есть, требуется послать нечто:
POST /....URL.../ HTTP/1.1
Authorization: Bearer token
Accept-Language: ru
Content-Type: charset=utf-8

т.е. теперь нужно как-то обернуть SOAP чтобы перед этим он посылал необходимый токен в HTTP запросе типа Authorization: Bearer token?
может кто подсказать код? не пойму как это сделать:(
 
Последнее редактирование:

Dzen

Новичок
@AnrDaemon, да, в буржунете нарыл тоже что-то подобное, но не получается реализовать :(

выдаёт:
Fatal error: Using $this when not in object context

PHP:
function soap_connect() {
$wsdl = 'https:.......?wsdl/';
  
$context = array('http' =>
        array(
            'header'  => 'Authorization: Bearer  ....token.....
        )
    );
    $soap_options = array(
        'encoding' => 'UTF-8',
        'stream_context' => stream_context_create($context)
    );
    try {
        $this->soap_client = new SoapClient($this->configuration[$wsdl], $soap_options);
    } catch (SoapFault $fault) {
        return FALSE;
    }
    return TRUE;
}

$test = soap_connect();
print_r($test);
как это можно совместить?
 

Dzen

Новичок
так токен тоже не передаётся:

PHP:
$wsdl = 'https:..............?wsdl/';


$client = new SoapClient($wsdl,
array(
    "exceptions" => 0,
      "trace" => 1,
        'encoding' => 'UTF-8',
     'stream_context' => stream_context_create(array('http' =>
    array('header' =>
    'Authorization: Bearer ......токен......',

    ')))));


$result = $client->check();
print_r($result);
 

AnrDaemon

Продвинутый новичок
new SoapHeader($wsdl, ''Authorization', "Bearer $token", false)

Хотя мне всё больше кажется, что это не то. Честно незнаком с SOAP.
 

Dzen

Новичок
@AnrDaemon, да, так не работает, выдаёт:

Код:
 [faultstring] => Authorization error
[faultcode] => SOAP-ENV:Client
[detail] => stdClass Object
(
[FaultResponse] => stdClass Object
(
[errorDetail] => Token not entered
)

тут дело именно в том, что токен надо отправлять в HTTP заголовке, а не в SOAP запросе.
Вроде бы правильно стримконтекст использовать, но не ясно как его делать, т.к. выдаёт ошибку:
http://phpclub.ru/talk/threads/Авторизация-по-http-перед-soap.81508/#post-739011

Кто-то решает вопрос Курлом + SOAP , но Курл выдаёт:

Fatal error: Uncaught SoapFault exception: [Client] SoapClient::__doRequest() returned non string value
 

AnrDaemon

Продвинутый новичок
Ну так смотри, что именно там nonstring…
Займись отладкой, …
 

Dzen

Новичок
не получается

делаю так:

PHP:
$wsdl = 'https://domen.com/service?wsdl/';
$token = '.....................';

ini_set("soap.wsdl_cache_enabled", "0");
$client = new SoapClient($wsdl,
                 array(
         "exceptions" => 0,
                  "trace" => 1,
                  "encoding" => 'UTF-8',
              'stream_context' => stream_context_create(
          array('http' =>
              array('header' => 'Authorization: Bearer $token')))));

$result = $client->check();
print_r($client);

Может неправильный синтаксис выше, насчёт Bearer:
array('header' => 'Authorization: Bearer $token')))));

получаю ответ что авторизация не прошла, т.к. Токен не введён:
print_r($client); выдаёт:

Код:
SoapClient Object
(
    [trace] => 1
    [_exceptions] =>
    [_encoding] => UTF-8
    [_stream_context] => Resource id #2
    [_soap_version] => 1
    [sdl] => Resource id #5
    [__last_request] => <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://domen.com/service"><SOAP-ENV:Body><ns1:CheckRequest/></SOAP-ENV:Body></SOAP-ENV:Envelope>

    [httpurl] => Resource id #7
    [__last_request_headers] => POST /service HTTP/1.1
Host: soap.domen.com
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.2.13
Content-Type: text/xml; charset=utf-8
SOAPAction: "https://domen.com/service/check"
Content-Length: 240


    [__last_response_headers] => HTTP/1.1 200 OK
Server: nginx
Date: Sun, 28 Feb 2016 12:00:54 GMT
Content-Type: text/xml
Transfer-Encoding: chunked
Connection: close
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff

    [__last_response] => <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Client</faultcode><faultstring>Authorization error</faultstring><detail><ns3:FaultResponse xmlns:ns3="http://domen.com/general"><requestId>12345</requestId><errorCode>12345</errorCode><errorDetail>Token not entered</errorDetail></ns3:FaultResponse></detail></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

    [__soap_fault] => SoapFault Object
        (
            [message:protected] => Authorization error
            [string:private] =>
            [code:protected] => 0
            [file:protected] => api.php
            [line:protected] => 53
            [trace:private] => Array
                (
                    [0] => Array
                        (
                            [function] => __call
                            [class] => SoapClient
                            [type] => ->
                            [args] => Array
                                (
                                    [0] => check
                                    [1] => Array
                                        (
                                        )

                                )

                        )

                    [1] => Array
                        (
                            [file] => api.php
                            [line] => 53
                            [function] => check
                            [class] => SoapClient
                            [type] => ->
                            [args] => Array
                                (
                                )

                        )

                )

            [faultstring] => Authorization error
            [faultcode] => SOAP-ENV:Client
            [detail] => stdClass Object
                (
                    [FaultResponse] => stdClass Object
                        (
                            [requestId] => 12345
                            [errorCode] => 12345
                            [errorDetail] => Token not entered
                        )

                )

        )

)
 

Dzen

Новичок
нашёл что в php 5.2. но в каких точно версиях не ясно, есть баг:
https://bugs.php.net/bug.php?id=41051

но даже заменяя
PHP:
 array('http' =>
              array('header' => 'Authorization: Bearer $token')))));
на
PHP:
              array('http' =>
                 array('header' =>
                      array('Authorization' => 'Bearer $token'))))));
всё равно получаем такую же ошибку:
[errorDetail] => Token not entered

хотя у меня версия 5.2.13, на 5.2.17 тоже самое.
 

Dzen

Новичок
посмотрел по print_r($client) который приводил выше:

PHP:
      [httpurl] => Resource id #7
        [__last_request_headers] => POST /service HTTP/1.1
    Host: soap.domen.com
    Connection: Keep-Alive
    User-Agent: PHP-SOAP/5.2.13
    Content-Type: text/xml; charset=utf-8
    SOAPAction: "https://domen.com/service/check"
    Content-Length: 240

так у нас же никакой Authorization Bearer токен не передаётся?!
Получается stream_context не работает?
 

Dzen

Новичок
или он там и не должен быть? потому что это SoapClient Object, а токен надо передать ВНЕ SoapClient Object, а не внутри как это обычно деают по new SoapHeader.

хотя нет, именно там он и должен быть если передавать токен в HTTP заголовке.
 
Последнее редактирование:

Dzen

Новичок
по ходу эта проблема SOAP и stream_context, нашёл у буржуев такую же проблему:
https://bugs.php.net/bug.php?edit=1&id=49853

пока всё не перевёл, читаю.
если кто осилил подскажите плиз, как этот баг исправить.

I suspect that my problem is the same as the submitter, so I'm adding this as a comment instead of as a new issue. Again, this has to do with SoapClient, stream_context_create, and custom http headers.

Simple TESTCASE:
#!/usr/bin/php
<?php
$wsdl = 'http://www.w3schools.com/webservices/tempconvert.asmx?WSDL';
$apikey = 'this-must-still-be-here';
$c = stream_context_create(array(
'http' => array(
'header' => "apikey: this-must-still-be-here",
)
));
var_dump(stream_context_get_options($c));
$soap = new SoapClient($wsdl, array('stream_context' => $c));
var_dump(stream_context_get_options($soap->_stream_context));
unlink('/tmp/wsdl-'.get_current_user().'-d4e1ad3ff31424862843f4814eade4a8');
?>

As you can see from running the code, if the SoapClient needs to download a new WSDL (function get_sdl() called on line 2679 of ext/soap/soap.c - PHP source code is version 5.3.8, function defined at line 3154 of ext/soap/php_sdl.c), AND also the 'protocol_version' option isn't explicitly set in the stream_context (line 3267, file ext/soap/php_sdl.c), it sets the 'protocol_version' option (3271), and sets up a new header - "Connection: close" (3273). The following if statement (3276) sets the http "header" option to the value of that string (3286) - effectively overwriting any custom headers that were supplied.

The following if statement (3291) might be making an effort to prevent this (3294,3307), but is ineffective (operating on a pointer to the original I suspect; any copy should be made at the point of initialization, and this might not be copying the context structure itself at all).

So - userland mitigation:
Set the http 'protocol_version' explicitly to 1.1 in your code, and append the "\r\nConnection: close" header to your custom headers.
example:
$context = stream_context_create(array(
'http'=>array(
'protocol_version' => 1.1,
'header' => "token: 85E91AAC-7A4A-11E0-B46B-78E7D1E19752\r\n" .
"Connection: close"
)
));

Thoughts about fixing it:
It appears to be safe to pass the Connection: close header implicitly - so the simple solution of appending instead of replacing the custom headers could be good enough, without having to copy, backup, and restore the original context itself. Should that be the easier solution it should be preferred. If the simple solution is used, it would also be nice to have it in the docs.
 
Последнее редактирование:

Dzen

Новичок
на php 5.4.41 stream_context вместе c SOAP работает! и в [__last_request_headers] есть токен по print_r($client);

на 5.2.13 - 17 - не работает stream_context с SOAP. Но на многих серверах он стоит:(, как можно пофиксить?
 
Последнее редактирование:

Yoskaldyr

"Спамер"
Партнер клуба
Это уже как-бы совсем старая и не поддерживаемая версия. Поэтому единственный правильный ответ - обновиться на версию по-новое. Если нет возможности обновиться, то или терпеть, или смотреть исходники пхп править ошибку и собирать руками пофикшенный пхп 5.2, но в любом случае страдать.
 

AnrDaemon

Продвинутый новичок
Даваще. >.<
Ну хотя бы 5.3 что ли… Идеально 5.6, естественно.
 

Dzen

Новичок
да, поставил 5.3.5 и всё заработало, именно баг soap'a был.

хотел поставить новее из http://windows.php.net/download/
но уже в 2-х версиях нету php5apache.dll
а он нужен
запускаю на денвере под виндой-хп
 

A1x

Новичок
запускаю на денвере под виндой-хп
мсье любитель древностей)) насколько помню php5apache.dll это апачевский mod_php, который давно никто не использует потому что есть php-fpm который тоже можно запускать под апачем, хотя насчет винды не знаю... в любом случае какой смысл наступать на грабли, которые возможно давно исправлены в новых версиях
 
  • Like
Реакции: Dzen
Сверху