1

我在 php 中有这个(工作)示例:

$usernameToken = new SoapHeaderUsernameToken( $password, $username );
$soapHeaders[] = new SoapHeader("urn:localhost-catalog", 'UsernameToken', $usernameToken);

$client = new SoapClient("my_client_url");
$client->__setSoapHeaders( $soapHeaders );

但我想要一个 Ruby 的客户。我正在使用 savon gem,它可以为您进行身份验证。到目前为止,我有以下方法:

def my_soap_client
    client = Savon::Client.new do
        wsdl.document = "my_client_url"
    end

    client.wsse.credentials "usr", "pass" # Adding :digest doesn't seem to help

    return client
end

# I make requests like this:
my_soap_client.request :wsdl, "getProductGroups", :parent_id => 1

我可以成功列出可用操作,但如果我尝试发出请求,结果始终是"(SOAP-ENV:Client) Missing parameter"。此外,我是否通过正确的凭据或伪造的凭据也没关系。这就是为什么我认为身份验证失败的原因。

希望有人能帮忙!

4

2 回答 2

2

使用Wireshark 嗅探器,我截获了您的 PHP 代码发送的 SOAP 请求。它看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
                   xmlns:ns1="urn:examples:helloservice"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:ns2="urn:localhost-catalog"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
                   SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Header>
    <ns2:UsernameToken>
      <Password>pass</Password>
      <Username>login</Username>
    </ns2:UsernameToken>
  </SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <ns1:getProductGroups>
      <parentId>1</parentId>
    </ns1:getProductGroups>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

在这里您可以看到此代码为 UsernameToken 标头添加了 ns2 命名空间。

然后我截获了发送 Ruby 代码的 SOAP 请求:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:wsdl="http://www.ecerami.com/wsdl/HelloService.wsdl"
              xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <wsse:UsernameToken wsu:Id="UsernameToken-1"
                          xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
        <wsse:Username>usr</wsse:Username>
        <wsse:Password
                Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
          pass
        </wsse:Password>
      </wsse:UsernameToken>
    </wsse:Security>
  </env:Header>
  <env:Body>
    <wsdl:getProductGroups>
       <parentId>1</parentId>
    </wsdl:getProductGroups>
  </env:Body>
</env:Envelope>

您可以看到差异 - UsernameToken 被包装到 Security 元素中,并且 UsernameToken 的命名空间是“http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd”

您的 PHP 代码使用自定义授权,而 savon 不通过 wsse 属性支持它。我查看了 Savon 源代码,它使用 Akami.wsse,并且在其代码中无法设置命名空间。这意味着您需要手动构建标题。这是 Ruby 中的代码,它可以生成您可能需要的 SOAP 请求:

require 'savon'

client = Savon::Client.new do
  wsdl.document = "test.wsdl"
end

client.request :wsdl, "sayHello" do
    soap.namespaces["xmlns:ns2"] = "urn:localhost-catalog"
    soap.header = { "ns2:UsernameToken" =>
                          {"Password" => "pass", "Username" => "login"}
                  }
    soap.body = { :parent_id => 1 }
end

它生成以下 SOAP 请求:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:wsdl="http://www.ecerami.com/wsdl/HelloService.wsdl"
              xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
              xmlns:ns2="urn:localhost-catalog">
  <env:Header>
    <ns2:UsernameToken>
      <Password>pass</Password>
      <Username>login</Username>
    </ns2:UsernameToken>
  </env:Header>
  <env:Body>
    <wsdl:sayHello>
      <parentId>1</parentId>
    </wsdl:sayHello>
  </env:Body>
</env:Envelope>

希望这可以帮助!

于 2011-12-13T15:38:06.290 回答
0

要在 Savon 中设置标题,请使用以下表达式:

client.request :wsdl, "getProductGroups" do
    soap.header = { :header_key => "your header goes here",
                    :attributes! => { :attr => "your attribute" }
                  }
    soap.body = { :parent_id => 1 }
end

也许这对你有帮助。

-英石

于 2011-12-11T19:23:42.643 回答