PHP 没有 Soap 客户端的 Soap 服务器

PHP Soap Server without Soap Client

有几个星期,我一直在尝试在 php 中创建一个 soap 服务器,它首先提供一个带有身份验证 header 的 wsdl,然后它只接受经过身份验证的用户要求。但我只是让它在没有身份验证的情况下完全工作。我所做的每一次搜索和我找到的每一个解决方案都包含一个 SoapClient,Zend_Soap_Client,nu_soap_client(你的名字)和我的 [=51= 周围的某种包装器 class ] 或仅在客户端添加用户名和密码。 但在我的解决方案中,只有服务器在 php 中,客户端是用 java 等编写的各种程序, 而不是 在 php 中。这是我的代码(我在这里使用 zend,但想法在普通 php 上是相同的)wsdl 生成和服务器部分:

use Zend\Soap\AutoDiscover as Zend_Soap_AutoDiscover;
use Zend\Soap\Server as Zend_Soap_Server;

 if (isset($_GET['wsdl'])) {
    $autodiscover = new Zend\Soap\AutoDiscover();
    $autodiscover->setClass('MyClass');
    $autodiscover->setUri('http://Myclass/path/');
    $autodiscover->handle();
     exit;
 }

 $server = new Zend_Soap_Server(null, array(
     'uri' => 'http://Myclass/path/',
 ));

 $server->setClass('Myclass');
 $server->handle();

我还使用了 piotrooo 的 wsdl 生成器和普通的 php soap 库,如下所示:

use WSDL\WSDLCreator;
// use WSDL\XML\Styles\DocumentLiteralWrapped;

 if (isset($_GET['wsdl'])) {
     $wsdl = new WSDL\WSDLCreator('Myclass', 'http://Myclass/path/');
     $wsdl->setNamespace("http://Myclass/path/");
     $wsdl->renderWSDL();
     exit;
 }

 $server = new SoapServer(null, array(
     'uri' => 'http://Myclass/path/',
    //  'style' => SOAP_DOCUMENT,
    //  'use' => SOAP_LITERAL,
 ));
 $server->setClass('Myclass');
 $server->handle();

还有我的class:

class Myclass
{
public function __construct()
    {
      /*some db stuff with doctrine*/
    }
    /**
     * @param string $id
     * @return object
     */
    public function Id($id)
    {
       /*I'm using doctrine to fetch data from db and then return an object/or array*/
    }
}

最后这是自动生成的 wsdl:

<definitions name="Myclass" targetNamespace="http://Myclass/path/"><types><xsd:schema targetNamespace="http://Myclass/path/"/></types><portType name="MyclassPort"><operation name="Id"><documentation>Id</documentation><input message="tns:IdIn"/><output message="tns:IdOut"/></operation></portType><binding name="MyclassBinding" type="tns:MyclassPort"><soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/><operation name="Id"><soap:operation soapAction="http://Myclass/path/#Id"/><input><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://Myclass/path/"/></input><output><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://Myclass/path/"/></output></operation></binding><service name="MyclassService"><port name="MyclassPort" binding="tns:MyclassBinding"><soap:address location="http://Myclass/path/"/></port></service><message name="IdIn"><part name="id" type="xsd:string"/></message><message name="IdOut"><part name="return" type="xsd:struct"/></message></definitions>

每个生成器的注释各不相同。 我也尝试过 nusoap 但我很失望,因为它是纯粹的 class 方法发现。我必须补充一点,我正在使用 soapui 测试服务的使用情况(这就是为什么我不想要 php 的 SoapClient 或等效示例的原因)。 最后我还必须说,我尝试了在我的 class 中添加身份验证方法的解决方案并且这有效 但是 这并没有阻止未经身份验证用户访问 ws.

一些额外的信息。根据我的研究,我发现的每个答案都是关于 SOAPClient 的,例如 $client->__addheader() 函数等。请告诉我我是否错了,或者这不能用 [=50= 完成] 因为我必须找其他人用另一种编程语言为我做这件事 Java 等等

提前致谢

季米特里斯

您可以让您的客户端在基本上 3 个地方发送身份验证信息:SOAP Body、SOAP Headers、HTTP Headers。在这三个中,我认为合适的是 SOAP Headers,所以我将在本示例中使用它。 (请注意,我正在使用一些自定义身份验证 header 格式,但我建议您研究 WS-Security 协议。)

class Myclass
{
    public function __construct()
    {
       /*some db stuff with doctrine*/
    }

    public function Auth($auth)
    {
       if (! $this->validateUser($auth->Username, $auth->Password)) {
           throw new SoapFault('Client.Authentication', 'Invalid username or password');
       }
    }

    /**
     * @param string $id
     * @return object
     */
    public function Id($id)
    {
        /*...*/
    }

    private function validateUser($user, $password)
    {
        /*...*/
    }
}

现在对于客户端:我认为您作为服务器角色不必担心哪些特定客户端会使用您的服务,只是因为您无法考虑所有客户端。这就是为什么有标准,可以从特定的实现中抽象出来。因此,只要您遵循标准,您就可以相信 任何 客户端都会遵守。幸运的是,SOAP header 是标准的一部分。

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <SOAP-ENV:Header>
        <Auth>
            <Username>foo</Username>
            <Password>bar</Password>
        </Auth>
    </SOAP-ENV:Header>
   <SOAP-ENV:Body>
        <Id>4</Id>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

这就是您期望收到的 XML,与客户无关。


最后,WSDL。据我所知,Zend Autodiscover 和 piotrooo 的库都不支持 headers 定义。显然 Yii 的 CWebService 可以(使用适当的方法注释)。

在最坏的情况下,您可以自己编写 WSDL,或者根据您选择的库生成的 WSDL 对其进行改编。

链接:


更新:

class Auth
{
    /**
     * @soap
     * @var string
     */
    public $Username;

    /**
     * @soap
     * @var string
     */
    public $Password;
}

class MyClass
{
    private $authenticated = false;    

    public function Auth($auth)
    {
        if ($this->validateUser($auth->Username, $auth->Password)) {
            $this->authenticated = true;
        }
    }

    /**
     * @soap
     * @header Auth $auth
     * @param string $id
     * @return object
     */
    public function Id($id)
    {
        if (! $this->authenticated) {
            throw new SoapFault('Client.Authentication', 'Invalid username or password');
        }

        //return $id;
    }

    private function validateUser($user, $password)
    {
        return $user == 'foo';
    }

}

require __DIR__ . '/vendor/yiisoft/yii/framework/yii.php';
$URL = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'];

if (isset($_GET['wsdl'])) {
    $gen = new CWsdlGenerator();
    $wsdl = $gen->generateWsdl(MyClass::class, $URL);
    header("Content-type: text/xml; charset=utf-8");
    echo $wsdl;
}
else {
    $server = new SoapServer($URL . '?wsdl');
    $server->setClass(MyClass::class);
    echo $server->handle();
}

这确实与我之前提供的示例 XML 的预期一样有效。 (您只需将 <Id>4</Id> 更改为 <Id><id>4</id></Id>)。