使用 PHP POST 到 Web 服务的摘要式身份验证的客户端部分

Posted

技术标签:

【中文标题】使用 PHP POST 到 Web 服务的摘要式身份验证的客户端部分【英文标题】:Client part of the Digest Authentication using PHP POST to Web Service 【发布时间】:2013-09-06 19:13:00 【问题描述】:

我正在尝试发布到 Web 服务(不是 RESTful)并通过 php 获得响应。但是,该 Web 服务需要摘要式身份验证。

我一直在网上搜索,发现大多数讨论和文章都是关于相反的方式(向用户请求摘要身份验证),而不是使用 PHP 响应它。

我可以使用该线程提供的代码生成摘要响应:HTTP Digest authenticating in PHP,但问题是与(或不?)POST 数据一起发送它。

这是我正在使用的代码:

$domain = "https://api.example.com";
$uri = "/ws.asmx/do";

// get headers
$response1_array = get_headers($web_service_url);
// get request part of digest auth
$response1 = $response1_array[5];
// get things behind "WWW-Authenticate:"
$response1 = substr($response1, 18);

// response() is a invented function to calculate the response according to the RFC2617
$response2 = response($response1, "username", "password", "GET", $uri);

// manually add some headers for POST, and fill out the parts that the calculation function missed
// for auth
$header =  
        "Host: api.example.com\r\n" .
        "Content-Type: application/x-www-form-urlencoded\r\n" .
        "Authorization: " . $response2 . ", nc=\"00000001\", opaque=\"0000000000000000\"" . "\r\nContent-Length: 0\r\n\r\n";

// echo the response from server
echo do_post_request($web_service_url, "", $header);

function do_post_request($url, $data, $optional_headers = null)

  $params = array('http' => array(
              'method' => 'POST',
              'content' => $data
            ));
  if ($optional_headers !== null) 
    $params['http']['header'] = $optional_headers;
  
  $ctx = stream_context_create($params);

  $fp = fopen($url, 'rb', false, $ctx);

  if (!$fp) 
    throw new Exception("Problem with $url");
  
  $response = stream_get_contents($fp);
  if ($response === false) 
    throw new Exception("Problem reading data from $url");
  
  return $response;

在回复中:

$response1(from web service):
    Digest realm="example.com", nonce="OS82LzIwMTMgMTI6MDI6NDYgUE0", opaque="0000000000000000", stale=false, algorithm=MD5, qop="auth"

$response2(I calculated given the server response):
    Digest username="username", realm="example.com", nonce="OS82LzIwMTMgMTI6MDI6NDYgUE0", uri="/ws.asmx/do", cnonce="1378494106", nc="1", response="0f96788854cf2098ba22c6121529d7de", qop="auth"

the final (2nd) response from server:
    Warning: fopen(https://api.example.com/ws.asmx/do): failed to open stream: HTTP request failed! HTTP/1.1 500 Internal Server Error in ...

我不明白为什么在这种情况下服务器会响应 500 错误。代码有什么问题吗?或者有没有人遇到过这个问题并解决了它并可以帮助我解决问题?

问候,

米洛

【问题讨论】:

【参考方案1】:

经过 2 天的摸索,最终用 cURL 解决了这个问题。我想这是第一次发布一段现成的用于摘要身份验证的 PHP 代码,希望它可以帮助和我在同一个沟里的人。

代码:

<?php

error_reporting(E_ALL); 
ini_set( 'display_errors','1');

$url = "https://api.example.com/ws.asmx/do";
$username = "username";
$password = "pwd";
$post_data = array(
        'fieldname1' => 'value1',
        'fieldname2' => 'value2'
  );

$options = array(
        CURLOPT_URL            => $url,
        CURLOPT_HEADER         => true,    
        CURLOPT_VERBOSE        => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_SSL_VERIFYPEER => false,    // for https
        CURLOPT_USERPWD        => $username . ":" . $password,
        CURLOPT_HTTPAUTH       => CURLAUTH_DIGEST,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => http_build_query($post_data) 
);

$ch = curl_init();

curl_setopt_array( $ch, $options );

try 
  $raw_response  = curl_exec( $ch );

  // validate CURL status
  if(curl_errno($ch))
      throw new Exception(curl_error($ch), 500);

  // validate HTTP status code (user/password credential issues)
  $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  if ($status_code != 200)
      throw new Exception("Response with Status Code [" . $status_code . "].", 500);

 catch(Exception $ex) 
    if ($ch != null) curl_close($ch);
    throw new Exception($ex);


if ($ch != null) curl_close($ch);

echo "raw response: " . $raw_response; 

?>

【讨论】:

【参考方案2】:

如果你想用 PUT 方法发送 JSON 数据:

<?php

error_reporting(E_ALL); 
ini_set( 'display_errors','1');

$url = "https://api.example.com/ws.asmx/do";
$username = "username";
$password = "pwd";
$post_data = array(
        'fieldname1' => 'value1',
        'fieldname2' => 'value2'
  );

$options = array(
        CURLOPT_URL            => $url,
        CURLOPT_HEADER         => true,    
        CURLOPT_VERBOSE        => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_SSL_VERIFYPEER => false,    // for https
        CURLOPT_USERPWD        => $username . ":" . $password,
        CURLOPT_HTTPAUTH       => CURLAUTH_DIGEST,
        CURLOPT_CUSTOMREQUEST  => "PUT",
        CURLOPT_POSTFIELDS     => json_encode($post_data) ,
);

$ch = curl_init();

curl_setopt_array( $ch, $options );

try 
  $raw_response  = curl_exec( $ch );

  // validate CURL status
  if(curl_errno($ch))
      throw new Exception(curl_error($ch), 500);

  // validate HTTP status code (user/password credential issues)
  $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  if ($status_code != 200)
      throw new Exception("Response with Status Code [" . $status_code . "].", 500);

 catch(Exception $ex) 
    if ($ch != null) curl_close($ch);
    throw new Exception($ex);


if ($ch != null) curl_close($ch);

echo "raw response: " . $raw_response; 

?>

【讨论】:

以上是关于使用 PHP POST 到 Web 服务的摘要式身份验证的客户端部分的主要内容,如果未能解决你的问题,请参考以下文章

使用带有摘要身份验证、jquery ajax 调用的 RestFul PHP Web 服务

php与web页面交互

如何获取(收听)POST 和 GET 方法发送到我的服务器 PHP

为啥在 Django 中需要基本或摘要式身份验证?

无法使用 Postman 将 POST 变量发送到 localhost 上的 php 脚本

使用 POST 将用户信息存储在 Web 服务器 IOS 应用程序中