Perl LWP не авторизован, пока Curl Ok

Я пытаюсь воссоздать рабочую команду CURL с LWP в Perl, и я получаю 401 несанкционированную ошибку от LWP. Команда отправляет JSON на определенный URL, как показано в приведенном ниже коде. Полное доменное имя, IP-адрес, порт и путь к серверу являются правильными и идентичными для curl и Perl, как и учетные данные и область. Любая помощь будет оценена - спасибо!

Ниже приведен рабочий синтаксис в выходных данных отладки и c URL:

#curl -v -k -u "USERNAME:PASSWORD" -X POST <SERVER_URL> -d '<JSON CONTENT>';

* About to connect() to <SERVER_URL> port 443 (#0)
*   Trying <SERVER_IP>... connected
* Connected to <SERVER_URL> (<SERVER_IP>) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* warning: ignoring value of ssl.verifyhost
* skipping SSL peer certificate verification
* SSL connection using TLS_DHE_RSA_WITH_AES_256_CBC_SHA
* Server certificate:
*       subject: [REDACTED]
*       start date: Apr 21 00:00:00 2016 GMT
*       expire date: Apr 21 23:59:59 2019 GMT
*       common name: <SERVER_URL>
*       issuer: [REDACTED]
* Server auth using Basic with user '<USERNAME>'
> POST <SERVER_PATH> HTTP/1.1
> Authorization: Basic <BASE64-ENCODED USERNAME:PASSWORD>
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: <SERVER_URL>
> Accept: */*
> Content-Length: 144
> Content-Type: application/x-www-form-urlencoded
> 
< HTTP/1.1 200 OK
< Date: Fri, 13 May 2016 13:48:42 GMT
< Server: Apache
< Content-Type: application/json
< Content-Length: 256
< 
* Connection #0 to host <SERVER_URL> left intact
* Closing connection #0

Обновлен код Perl и вывод в соответствии с предложением Штеффена. Я исправил ошибку первоначального цитирования, а также добавил заголовок Accept в команду LWP post:

use strict;
use warnings;
use LWP::UserAgent;
use Data::Dumper;

my $server_root_with_port = "<FQDN>:443";
my $url = "<SERVER_URL>";
my $realm = "<SERVER_REALM>";
my $json = "<JSON CONTENT>";
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 });
$ua->credentials($server_root_with_port,$realm,$username=>$password);
$response = $ua->post($url, 'Content-Type' => 'application/x-www-form-urlencoded', 'Accept' => '*/*', 'Content' => $json);
print Dumper $response;

exit;

$VAR1 = bless( {
         '_protocol' => 'HTTP/1.1',
         '_content' => '',
         '_rc' => '400',
         '_headers' => bless( {
                    'connection' => 'close',
                    'client-response-num' => 1,
                    'date' => 'Mon, 16 May 2016 14:18:59 GMT',
                    'client-ssl-cert-issuer' => '[REDACTED]',
                    'client-ssl-cipher' => 'AES128-SHA256',
                    'client-peer' => '<SERVER_IP>:443',
                    'content-length' => '0',
                    '::std_case' => {
                              'client-date' => 'Client-Date',
                              'client-response-num' => 'Client-Response-Num',
                              'client-ssl-cert-subject' => 'Client-SSL-Cert-Subject',
                              'client-ssl-cert-issuer' => 'Client-SSL-Cert-Issuer',
                              'client-ssl-cipher' => 'Client-SSL-Cipher',
                              'client-peer' => 'Client-Peer',
                              'client-ssl-socket-class' => 'Client-SSL-Socket-Class'
                            },
                    'client-date' => 'Mon, 16 May 2016 14:18:59 GMT',
                    'client-ssl-cert-subject' => '[REDACTED]',
                    'server' => 'Apache',
                    'client-ssl-socket-class' => 'IO::Socket::SSL'
                      }, 'HTTP::Headers' ),
         '_previous' => bless( {
                     '_protocol' => 'HTTP/1.1',
                     '_content' => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
                            <html><head>
                            <title>401 Unauthorized</title>
                            </head><body>
                            <h1>Unauthorized</h1>
                            <p>This server could not verify that you
                            are authorized to access the document
                            requested.  Either you supplied the wrong
                            credentials (e.g., bad password), or your
                            browser doesn\'t understand how to supply
                            the credentials required.</p>
                            </body></html>
                            ',
                     '_rc' => '401',
                     '_headers' => bless( {
                                'connection' => 'close',
                                'client-response-num' => 1,
                                'date' => 'Mon, 16 May 2016 14:18:59 GMT',
                                'client-ssl-cert-issuer' => '[REDACTED]',
                                'client-ssl-cipher' => 'AES128-SHA256',
                                'client-peer' => '<SERVER_IP>:443',
                                'content-length' => '381',
                                '::std_case' => {
                                          'client-date' => 'Client-Date',
                                          'client-response-num' => 'Client-Response-Num',
                                          'client-ssl-cert-subject' => 'Client-SSL-Cert-Subject',
                                          'title' => 'Title',
                                          'client-ssl-cert-issuer' => 'Client-SSL-Cert-Issuer',
                                          'client-ssl-cipher' => 'Client-SSL-Cipher',
                                          'client-peer' => 'Client-Peer',
                                          'client-ssl-socket-class' => 'Client-SSL-Socket-Class'
                                        },
                                'client-date' => 'Mon, 16 May 2016 14:18:59 GMT',
                                'content-type' => 'text/html; charset=iso-8859-1',
                                'client-ssl-cert-subject' => '[REDACTED]',
                                'www-authenticate' => 'Basic realm="<SERVER_REALM>"',
                                'title' => '401 Unauthorized',
                                'server' => 'Apache',
                                'client-ssl-socket-class' => 'IO::Socket::SSL'
                                  }, 'HTTP::Headers' ),
                     '_msg' => 'Unauthorized',
                     '_request' => bless( {
                                '_content' => '<JSON_CONTENT>',
                                '_uri' => bless( do{\(my $o = '<SERVER_URL>')}, 'URI::https' ),
                                '_headers' => bless( {
                                               'user-agent' => 'libwww-perl/6.15',
                                               'content-type' => 'application/x-www-form-urlencoded',
                                               'accept' => '*/*',
                                               'content-length' => 144,
                                               '::std_case' => {
                                                     'if-ssl-cert-subject' => 'If-SSL-Cert-Subject'
                                                       }
                                             }, 'HTTP::Headers' ),
                                '_method' => 'POST',
                                '_uri_canonical' => $VAR1->{'_previous'}{'_request'}{'_uri'}
                                  }, 'HTTP::Request' )
                       }, 'HTTP::Response' ),
         '_msg' => 'Bad Request',
         '_request' => bless( {
                    '_protocol' => undef,
                    '_content' => '<JSON_CONTENT>',
                    '_uri' => bless( do{\(my $o = '<SERVER_URL>')}, 'URI::https' ),
                    '_headers' => bless( {
                                   'user-agent' => 'libwww-perl/6.15',
                                   'content-type' => 'application/x-www-form-urlencoded',
                                   'accept' => '*/*',
                                   'content-length' => 144,
                                   'authorization' => '<BASE64-ENCODED USERNAME:PASSWORD>',
                                   '::std_case' => {
                                         'if-ssl-cert-subject' => 'If-SSL-Cert-Subject'
                                           }
                                 }, 'HTTP::Headers' ),
                    '_method' => 'POST',
                    '_uri_canonical' => $VAR1->{'_request'}{'_uri'}
                      }, 'HTTP::Request' )
           }, 'HTTP::Response' );

Perl Revision # 1:

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common;
use Data::Dumper;

my $fqdn_port = "<FQDN>:443";
my $url       = "<URL>";
my $realm     = "<REALM>";
my $username  = "<USERNAME>";
my $password  = "<PASSWORD>";
my $json      = "<JSON_CONTENT>";

my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 });

#$ua->credentials($fqdn_port,$realm,$username=>$password);
#my $response = $ua->post($url, 'Content-Type' => 'application/x-www-form-urlencoded', 'Accept' => '*/*', Content => $json);

my $request = HTTP::Request->new('POST',$url);
$request->header('Content-Type' => 'application/x-www-form-urlencoded', 'Accept' => '*/*');
$request->authorization_basic($username,$password);
$request->content($json);

my $response = $ua->request($request);

print Dumper $response;

exit;


$VAR1 = bless( {
         '_protocol' => 'HTTP/1.1',
         '_content' => '',
         '_rc' => '400',
         '_headers' => bless( {
                    'connection' => 'close',
                    'client-response-num' => 1,
                    'date' => 'Mon, 16 May 2016 15:41:10 GMT',
                    'client-ssl-cert-issuer' => '[REDACTED]',
                    'client-ssl-cipher' => 'AES128-SHA256',
                    'client-peer' => '<SERVER_IP>:443',
                    'content-length' => '0',
                    '::std_case' => {
                              'client-date' => 'Client-Date',
                              'client-response-num' => 'Client-Response-Num',
                              'client-ssl-cert-subject' => 'Client-SSL-Cert-Subject',
                              'client-ssl-cert-issuer' => 'Client-SSL-Cert-Issuer',
                              'client-ssl-cipher' => 'Client-SSL-Cipher',
                              'client-peer' => 'Client-Peer',
                              'client-ssl-socket-class' => 'Client-SSL-Socket-Class'
                            },
                    'client-date' => 'Mon, 16 May 2016 15:41:10 GMT',
                    'client-ssl-cert-subject' => '[REDACTED]',
                    'server' => 'Apache',
                    'client-ssl-socket-class' => 'IO::Socket::SSL'
                      }, 'HTTP::Headers' ),
         '_msg' => 'Bad Request',
         '_request' => bless( {
                    '_content' => '<JSON_CONTENT>',
                    '_uri' => bless( do{\(my $o = '<URL>')}, 'URI::https' ),
                    '_headers' => bless( {
                                   'user-agent' => 'libwww-perl/6.15',
                                   'content-type' => 'application/x-www-form-urlencoded',
                                   'accept' => '*/*',
                                   '::std_case' => {
                                         'if-ssl-cert-subject' => 'If-SSL-Cert-Subject'
                                           },
                                   'authorization' => 'Basic <BASE64-ENCODED USERNAME:PASSWORD>'
                                 }, 'HTTP::Headers' ),
                    '_method' => 'POST',
                    '_uri_canonical' => $VAR1->{'_request'}{'_uri'}
                      }, 'HTTP::Request' )
           }, 'HTTP::Response' );

2 ответа

TL;TR: всегда use strict!!

$response = $ua->post($url, Content-Type => 'application/json', Content => $json);

Вы пропустили цитировать вокруг Content-Type который был бы обнаружен use strict, Этот результат этого странного заголовка 0 вы видите в выводе отладки:

 'content-type' => 'application/x-www-form-urlencoded',
 '0' => 'application/json',
 'content-length' => 144,

И это также означает, что настройка типа контента неверна. Это вместе приводит к тому, что сервер не принимает ваш запрос:

  '_rc' => '400',
  ...
  '_msg' => 'Bad Request',

Чтобы понять, что здесь происходит, посмотрите, что Perl на самом деле видит в таком коде:

 $ perl -MO=Deparse -e 'my %x = (Content-Type => 1, Foo => 2 )'
 my(%x) = ('Content' - 'Type', 1, 'Foo', 2);

Это показывает, что это будет интерпретировать без кавычек Content-Type как 'Content' - 'Type', И так как вычитание не определено для строк, они будут приведены к целому числу, т.е. 0, Что означает, что результат 0 (0-0).

При использовании строгого вы получаете вместо этого:

perl -Mstrict -e 'my %x = (Content-Type => 1, Foo => 2 )'                                                                                                                                 
Bareword "Content" not allowed while "strict subs" in use at -e line 1.                                                                                                                                           
Execution of -e aborted due to compilation errors.  

Нашел решение - возможно, не самое элегантное, но оно делает свое дело. Закончилось использование HTTP::Request::Common, чтобы обойти проблему авторизации, и отменить кавычки в переменной JSON, чтобы уменьшить количество 400 неверных запросов и успех - получение правильного возврата с сервера! Спасибо за помощь @steffen_ullrich.

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common;
use Data::Dumper;

my $fqdn_port = "<FQDN>:443";
my $url       = "<URL>";
my $realm     = "<REALM>";
my $username  = "<USERNAME>";
my $password  = "<PASSWORD>";
my $json      = '<JSON_CONTENT>';

my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 });
my $request = HTTP::Request->new('POST',$url);
$request->header('Content-Type' => 'application/x-www-form-urlencoded', 'Accept' => '*/*');
$request->authorization_basic($username,$password);
$request->content($json);

my $response = $ua->request($request);
print Dumper $response;
exit;


$VAR1 = bless( {
         '_protocol' => 'HTTP/1.1',
         '_content' => '<RETURN JSON FROM SERVER>',
         '_rc' => '200',
         '_headers' => bless( {
                    'connection' => 'close',
                    'client-response-num' => 1,
                    'date' => 'Mon, 16 May 2016 16:07:07 GMT',
                    'client-ssl-cert-issuer' => '[REDACTED]',
                    'client-ssl-cipher' => 'AES128-SHA256',
                    'client-peer' => '<SERVER_IP>:443',
                    'content-length' => '233',
                    '::std_case' => {
                              'client-date' => 'Client-Date',
                              'client-response-num' => 'Client-Response-Num',
                              'client-ssl-cert-subject' => 'Client-SSL-Cert-Subject',
                              'client-ssl-cert-issuer' => 'Client-SSL-Cert-Issuer',
                              'client-ssl-cipher' => 'Client-SSL-Cipher',
                              'client-peer' => 'Client-Peer',
                              'client-ssl-socket-class' => 'Client-SSL-Socket-Class'
                            },
                    'client-date' => 'Mon, 16 May 2016 16:07:07 GMT',
                    'content-type' => 'application/json',
                    'client-ssl-cert-subject' => '[REDACTED]',
                    'server' => 'Apache',
                    'client-ssl-socket-class' => 'IO::Socket::SSL'
                      }, 'HTTP::Headers' ),
         '_msg' => 'OK',
         '_request' => bless( {
                    '_content' => '<JSON_CONTENT>',
                    '_uri' => bless( do{\(my $o = '<URL>')}, 'URI::https' ),
                    '_headers' => bless( {
                                   'user-agent' => 'libwww-perl/6.15',
                                   'content-type' => 'application/x-www-form-urlencoded',
                                   'accept' => '*/*',
                                   '::std_case' => {
                                         'if-ssl-cert-subject' => 'If-SSL-Cert-Subject'
                                           },
                                   'authorization' => 'Basic <BASE64-ENCODED USERNAME:PASSWORD>'
                                 }, 'HTTP::Headers' ),
                    '_method' => 'POST',
                    '_uri_canonical' => $VAR1->{'_request'}{'_uri'}
                      }, 'HTTP::Request' )
           }, 'HTTP::Response' );
Другие вопросы по тегам