如何在Ruby中创建urn:localhost-catalog SOAP标头?

时间:2011-12-11 11:42:18

标签: ruby soap header savon

我在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)缺少参数”。此外,如果我传递正确的凭据或假的凭证也没关系。这就是为什么我认为身份验证失败。

希望有人可以提供帮助!

2 个答案:

答案 0 :(得分:2)

使用Wireshark sniffer我拦截了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>

希望这有帮助!

答案 1 :(得分: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

也许这会对你有帮助。

-st