【问题标题】:Perl LWP Unauthorized While Curl Ok卷曲正常时 Perl LWP 未经授权
【发布时间】:2016-05-13 14:30:36
【问题描述】:

我正在尝试在 Perl 中使用 LWP 重新创建一个有效的 CURL 命令,但我从 LWP 收到 401 未经授权的错误。该命令将 JSON 发布到特定 URL,如下面的代码所示。服务器的 FQDN、IP、端口和路径在 curl 和 Perl 之间是正确的和相同的,凭据和领域也是如此。任何帮助将不胜感激 - 谢谢!

以下是 cURL 和调试输出中的工作语法:

#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

根据 Steffen 的建议更新了 Perl 代码和输出。我更正了初始引用错误,并将 Accept 标头添加到 LWP 发布命令:

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 修订 #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' );

【问题讨论】:

  • verify_hostname => 1 应该是 => 0,这将匹配 curl -k

标签: perl curl lwp


【解决方案1】:

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,

这也意味着content-type的设置是错误的。这一起导致服务器不接受您的请求:

  '_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)。

当使用 strict 时,你会得到:

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.  

【讨论】:

  • Steffen - 我按照您的建议更新了我的代码,用单引号将 Content-Type 括起来,因此生成的语法为 'Content-Type' => 'application/json' 并使用 strict 我不收到错误,但我仍然收到 401 未经授权的错误。
  • @dthumb:请更新您问题中的代码和调试输出以反映当前状态。
  • 按照建议的编辑更新了原始问题 - 不再看到格式错误的标头,但仍然从服务器获得 401 Unauthorized 返回。谢谢。
  • @dthumb:根据调试输出,当前请求再次返回“400 Bad request”(401 在您的授权之前)。可能是您缺少服务器期望的一些标头 - 特别是 curl 使用的“接受”。查看您正在使用的 API 文档,了解客户的预期。
  • 我从服务器附加了 Apache 日志 - 用户名没有在初始 LWP POST 中传递...我不确定如何添加它,或者它是否已经通过 UA 传递凭据。
【解决方案2】:

找到了一个解决方案——也许不是最优雅的,但它确实可以解决问题。最终使用 HTTP::Request::Common 来解决授权问题并反转对 JSON 变量的引用以减轻 400 Bad Request 和成功 - 从服务器获得正确的返回!感谢@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' );

【讨论】:

  • 对问题的更新应该作为对问题的更新,而不是作为答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-15
  • 1970-01-01
  • 2019-03-11
  • 2012-02-22
  • 2014-07-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多