【问题标题】:PHP Soap Server without Soap Client没有 Soap 客户端的 PHP Soap 服务器
【发布时间】:2016-07-06 18:13:59
【问题描述】:

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

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 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,但我很失望,因为它是纯类方法发现。我必须补充一点,我正在使用 soapui 测试服务的使用(这就是我不想要 php 的 SoapClient 或等效示例的原因)。 最后我还必须说,我尝试了在我的类中添加身份验证方法的解决方案,并且这有效但是这并没有阻止未经身份验证的用户访问 ws。

一些额外的信息。在我的研究中,我找到的每个答案都是关于 SOAPClient 例如 $client->__addheader() 函数等。请告诉我我是否错了,或者如果这不能用 PHP 完成,因为我必须找人否则用另一种编程语言(如 Java 等)为我做这件事。

提前致谢

迪米特里斯

【问题讨论】:

  • 一个 WSDL 应该是公开的。你为什么要阻止它公开?
  • 不,我没有暴露,因为它必须暴露!

标签: php web-services authentication zend-framework soap


【解决方案1】:

您可以让您的客户端在基本上 3 个位置发送身份验证信息:SOAP 正文、SOAP 标头、HTTP 标头。在这三个中,我认为正确的一个是 SOAP Headers,所以我将在这个示例中使用它。 (注意我使用了一些自定义的 auth 头格式,但我建议你研究一下 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 标头是标准的一部分。

<?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 的库都不支持头文件定义。显然 Yii 的 CWebService 可以(带有适当的方法注释)。

在最坏的情况下,您可以自己编写 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 一样正常工作。 (您只需将&lt;Id&gt;4&lt;/Id&gt; 更改为&lt;Id&gt;&lt;id&gt;4&lt;/id&gt;&lt;/Id&gt;)。

【讨论】:

  • 感谢您抽出宝贵时间回答。首先我必须问,如果我把 auth 函数既不把 zend 也不 piotrooo 的 lib 解析为一个肥皂头,所以生成的 wsdl 不包含正确的头,你是否能够实现它以及如何实现?至于我自己创建 wdsl 是我最后的解决方案,我正在努力避免它。例如,我正在考虑手动创建肥皂标题并将其与自动生成的连接起来。在接受您的答案之前,我会再试一次(我前段时间尝试过但没有成功)并在这里报告。再次感谢。
  • Zend 似乎不支持soap headers,piotrooo 显然支持,或者至少有一个generateHeaderIfNeeded 方法。我会调查的。另外,你试过我链接的 Yii 吗?如果这些选项都不起作用,我想你只需要在 wsdl 中添加一行。
  • WSDL 不谈,你试过这个类吗?例如,从 Soap UI 发布示例 xml 以进行测试?
  • 当我尝试 piotrooo 时,我没有找到这种方法,就我而言,在 piotrooo 上存在一个关于 auth 标头的未解决问题,我也对此提出了同样的问题。 yii 和side 都没有尝试过,我会试一试并尽快报告
  • 是的,是的,在再次查看我的 -deleted- 评论后,我意识到你所说的双 ID 是什么意思,但没关系,再次感谢
猜你喜欢
  • 1970-01-01
  • 2021-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多