PHP NTLM认证+ SOAP客户端

3

我遇到了一个需要实现NTLM身份验证保护的Web服务项目。

我尝试使用PHP的SoapClient

$client = new \SoapClient("http://hostname.com/webservice",
            array(
                'cache_wsdl' => WSDL_CACHE_NONE,
                'login' => "username",
                'password' => "password"
            ));

这会抛出以下错误:

PHP致命错误:SOAP-ERROR:解析WSDL时出错:无法从 'http://hostname.com/webservice' 加载:无法加载外部实体 "http://hostname.com/webservice"

我尝试了使用CURL:

curl --ntlm -u username:password "http://hostname.com/webservice" --verbose

这个功能按预期工作,并返回了正确的xml。

但是,SoapClient不支持NTLM身份验证。

有人知道关于PHP Soap+NTLM的信息吗?

提前感谢您的帮助。

2个回答

2

我最近也遇到了这个问题。

以下是我找到的解决方案:

创建NTLMStream.php文件,内容如下:

<?php
/*
* Original https://thomas.rabaix.net/blog/2008/03/using-soap-php-with-ntlm-authentication
* Modified by http://blogs.msdn.com/b/freddyk/archive/2010/01/19/connecting-to-nav-web-services-from-php.aspx:
*/

class NTLMStream
{
    private $path;
    private $mode;
    private $options;
    private $opened_path;
    private $buffer;
    private $pos;
    
    public function stream_open($path, $mode, $options, $opened_path) {
        $this->path = $path;
        $this->mode = $mode;
        $this->options = $options;
        $this->opened_path = $opened_path;
        $this->createBuffer($path);
        return true;
    }
    public function stream_close() {
        curl_close($this->ch);
    }
    public function stream_read($count) {
        if(strlen($this->buffer) == 0) {
            return false;
        }
        $read = substr($this->buffer,$this->pos, $count);
        $this->pos += $count;
        return $read;
    }
    public function stream_write($data) {
        if(strlen($this->buffer) == 0) {
            return false;
        }
        return true;
    }
    public function stream_eof() {
        return ($this->pos > strlen($this->buffer));
    }
    public function stream_tell() {
        return $this->pos;
    }
    public function stream_flush() {
        $this->buffer = null;
        $this->pos = null;
    }
    public function stream_stat() {
        $this->createBuffer($this->path);
        $stat = array(
            'size' => strlen($this->buffer),
        );
        return $stat;
    }
    public function url_stat($path, $flags) {
        $this->createBuffer($path);
        $stat = array(
            'size' => strlen($this->buffer),
        );
        return $stat;
    }
    private function createBuffer($path) {
        if($this->buffer) {
            return;
        }
        $this->ch = curl_init($path);
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
        curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($this->ch, CURLOPT_USERPWD, USERPWD);
        $this->buffer = curl_exec($this->ch);
        $this->pos = 0;
    }
}

class NTLMSoapClient extends \SoapClient
{
    function __doRequest($request, $location, $action, $version, $one_way = 0) {
        $headers = array(
            'Method: POST',
            'Connection: Keep-Alive',
            'User-Agent: PHP-SOAP-CURL',
            'Content-Type: text/xml; charset=utf-8',
            'SOAPAction: "'.$action.'"',
        );
        $this->__last_request_headers = $headers;
        $ch = curl_init($location);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POST, true );
        curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_USERPWD, USERPWD);
        $response = curl_exec($ch);
        return $response;
    }
    function __getLastRequestHeaders() {
        return implode("\n", $this->__last_request_headers)."\n";
    }
}

那么在您的test.php中:
<?php
define('USERPWD', 'domain\username:password');
require_once("NTLMStream.php");
stream_wrapper_unregister("https");
stream_wrapper_register("https", "NTLMStream");
$params = [
    'stream_context' => stream_context_create([
           'ssl' => [
            'ciphers'=>'RC4-SHA', 
            'verify_peer'=>false, 
            'verify_peer_name'=>false, 
            'allow_self_signed'=>true,
        ]]),
    'cache_wsdl' => WSDL_CACHE_NONE,
    'soap_version' => SOAP_1_1,
    'trace' => 1,
    'connection_timeout' => 180, 
    'features' => SOAP_SINGLE_ELEMENT_ARRAYS
];
$client = new NTLMSoapClient("https://hostname.com/webservice", $params);
$retVal = $client->ReadMultiple(...
...

将http://替换为https://:

stream_wrapper_register("http", "NTLMStream")
stream_wrapper_register("https", "NTLMStream")

在你的 test.php 文件中,parem 字段没有正确关闭。请更新你的答案,因为这对任何人都没有帮助。 - CreativeMinds

0

这个库对我很有帮助(我使用composer): https://packagist.org/packages/matejsvajger/ntlm-soap-client

$url = 'URL_TO_WEBSERVICE_WSDL';
$config = new matejsvajger\NTLMSoap\Common\NTLMConfig([
    'domain'   => 'domain',
    'username' => 'username',
    'password' => 'password'
]);

$client = new matejsvajger\NTLMSoap\Client($url, $config);

$response = $client->ReadMultiple(['filter'=>[], 'setSize'=>1]);

foreach ($response->ReadMultiple_Result->CRMContactlist as $entity) {
    print_r($entity);
}

它不适用于PHP 8。 - Marko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接