我已使用SOAPUI(www.soapui.org)成功连接到远程Web服务。但是,我无法从CF9.2成功调用它。
这是我的整个CFC功能。有动态变量,但我已经在soapUI界面中测试了输出,它可以工作:
<cffunction name="getOrganisation" access="remote" returnType="any" output="true">
<cfargument name="iPageNumber" type="any" required="false" default="0">
<cfargument name="iPageSize" type="any" required="false" default="0">
<cfargument name="bCurrentNamesOnly" type="boolean" required="false" default="1">
<cfargument name="bExcludeNotRtos" type="boolean" required="false" default="0">
<cfargument name="bExcludeRtoWithoutActiveRegistration" type="boolean" required="false" default="0">
<cfargument name="sFilter" type="any" required="false" default="">
<cfargument name="bIncludeCode" type="boolean" required="false" default="1">
<cfargument name="sRegistrationManagers" type="any" required="false" default="">
<cfargument name="sClassificationFilters" type="any" required="false" default="">
<cfargument name="sScheme" type="any" required="false" default="">
<cfset var endpoint = "https://ws.staging.training.gov.au/Deewr.Tga.WebServices/OrganisationService.svc/Organisation">
<cfsavecontent variable="soapBody">
<cfoutput>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://training.gov.au/services/"
xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<soapenv:Header/>
<soapenv:Body>
<ser:Search>
<ser:request>
<ser:PageNumber>#arguments.iPageNumber#</ser:PageNumber>
<ser:PageSize>#arguments.iPageSize#</ser:PageSize>
<ser:ClassificationFilters>
<ser:ClassificationFilter>
<ser:Scheme>#arguments.sScheme#</ser:Scheme>
<ser:Values>
<cfif len(arguments.sClassificationFilters)>
<cfloop list="#arguments.sClassificationFilters#" index="item">
<arr:string>#item#</arr:string>
</cfloop>
</cfif>
</ser:Values>
</ser:ClassificationFilter>
</ser:ClassificationFilters>
<ser:CurrentNamesOnly>#arguments.bCurrentNamesOnly#</ser:CurrentNamesOnly>
<ser:ExcludeNotRtos>#arguments.bExcludeNotRtos#</ser:ExcludeNotRtos>
<ser:ExcludeRtoWithoutActiveRegistration>#arguments.bExcludeRtoWithoutActiveRegistration#</ser:ExcludeRtoWithoutActiveRegistration>
<ser:Filter>#arguments.sFilter#</ser:Filter>
<ser:IncludeCode>#arguments.bIncludeCode#</ser:IncludeCode>
<ser:RegistrationManagers>
<cfif len(arguments.sRegistrationManagers)>
<cfloop list="#arguments.sRegistrationManagers#" index="item">
<arr:string>#item#</arr:string>
</cfloop>
</cfif>
</ser:RegistrationManagers>
</ser:request>
</ser:Search>
</soapenv:Body>
</soapenv:Envelope>
</cfoutput>
</cfsavecontent>
<cfhttp
url="#endpoint#"
method="post"
username="#variables.username#"
password="#variables.password#">
<cfhttpparam type="header" name="accept-encoding" value="no-compression" />
<cfhttpparam type="xml" value="#trim(soapBody)#"/>
</cfhttp>
<cfdump var="#cfhttp.FileContent#"><cfabort>
<cfreturn cfhttp.FileContent>
</cffunction>
运行它,我收到错误:
An error occurred when verifying security for the message.
以下是完整的返回xml
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">a:InvalidSecurity</faultcode>
<faultstring xml:lang="en-AU">An error occurred when verifying security for the message.</faultstring>
</s:Fault>
</s:Body>
</s:Envelope>
所以,这似乎是一个授权问题。
这是SoapUI请求屏幕:
那么,我如何构造cfhttp,或者cfinvoke来模拟soapUI调用呢?
SOAP请求XML
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://training.gov.au/services/" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<soapenv:Header/>
<soapenv:Body>
<ser:Search>
<ser:request>
<ser:PageNumber>0</ser:PageNumber>
<ser:PageSize>0</ser:PageSize>
<ser:ClassificationFilters>
<ser:ClassificationFilter>
<ser:Scheme></ser:Scheme>
<ser:Values>
<arr:string></arr:string>
</ser:Values>
</ser:ClassificationFilter>
</ser:ClassificationFilters>
<ser:CurrentNamesOnly>true</ser:CurrentNamesOnly>
<ser:ExcludeNotRtos>0</ser:ExcludeNotRtos>
<ser:ExcludeRtoWithoutActiveRegistration>0</ser:ExcludeRtoWithoutActiveRegistration>
<ser:Filter></ser:Filter>
<ser:IncludeCode>1</ser:IncludeCode>
<ser:RegistrationManagers>
<arr:string></arr:string>
</ser:RegistrationManagers>
</ser:request>
</ser:Search>
</soapenv:Body>
</soapenv:Envelope>
更多信息:
答案 0 :(得分:0)
我认为你需要在cfhttpparam标签中发布用户名和密码,而不是cfhttp标签中的属性。
答案 1 :(得分:0)
SOAPUI是一个很棒的工具,我最近在调试SOAP请求或将它们从静态.wsdl文件传输到ColdFusion组件时使用了很多。
首先要看的是错误信息本身:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">a:InvalidSecurity</faultcode>
<faultstring xml:lang="en-AU">An error occurred when verifying security for the message. </faultstring>
</s:Fault>
</s:Body>
</s:Envelope>
faultcode节点包含指向OASIS安全命名空间文档的链接:
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
在浏览器中查看,您可以查看所需的安全值,并阐明命名约定。因此,我们可以从中确认您需要通过以下格式发送以下内容:
如果无法访问您在SOAPUI中测试的请求,我们还可以看到用户名和密码值在属性窗口中可见(您从上面的界面抓取屏幕)。
这些值是否在SOAPUI中的请求的XML中设置?也许在SOAP请求标头中,如下所示:
<soapenv:Header>
<authInfo xsi:type="soap:authentication">
<Username xsi:type="xsd:string">?</Username>
<Password xsi:type="xsd:string">?</Password>
</authInfo>
</soapenv:Header>
如果是这种情况,您还需要在构建soapBody变量时在ColdFusion组件中包含此标头。
此外,您是否可以直接在浏览器中查看SOAP请求的URL以查看它所期望的变量?
https://ws.staging.training.gov.au/Deewr.Tga.WebServices/OrganisationService.svc/Organisation
CFC和soapBody的整体结构看起来不错,因此无法访问您在SOAPUI中运行的文件以查看它运行,调试它并提供答案会有点棘手。
如果您可以排除上述所有可能性但仍然存在问题,请与我们联系。
答案 2 :(得分:0)
是的,您最初没有通过Oasis安全标头。到目前为止,这里的答案可能不会起作用。 Oasis标准可以允许许多用户/密码/现时/摘要/创建和编码属性的各种组合。
正如您可能找到或未找到的那样,Axis 1库(CF9和下层用户Axis 1(版本1.1-1.4); CF10也有Axis 2(Axis版本1.6))。因此,您必须POST(cfhttp / http)来传递和接收Oasis标头。 Axis 1库会为尝试理解标题区域中的Oasis节点而哭泣。**
由于 手动发布,您只需在标题中构建Oasis标题,就像Matt Gifford的示例一样。
根据接收方的期望,您可能会遇到以下情况:
<wsse:Security soap:mustUnderstand="true" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<wsse:UsernameToken wsu:Id="UsernameToken-15">
<wsse:Username>
<!-- Removed-->
</wsse:Username>
<wsse:Password>
<!-- Removed-->
</wsse:Password>
<wsse:Nonce>
<!-- Removed-->
</wsse:Nonce>
<wsu:Created>2012-07-11T02:02:48.410Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
或者这(从早期包层方法预输出):
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" >
<wsse:Username>#Arguments.szUserName#</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0##PasswordText">#Arguments.szPassword#</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
或此(最终输出):
<wsse:Security soapenv:mustUnderstand="1"xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-974900"xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>SuperJellyMan</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">jellybeanboom</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">SmVsbHlCZWFuQm9vbTk3NDkwMA==</wsse:Nonce>
<wsu:Created>2012-07-26T17:00:34Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
依此类推。我见过人们调用java库来更动态地执行此操作。我只是构建了一些简单的方法来构建我的随机数并格式化我的xml时间戳。但基本上,构建某种函数或其他辅助方法来将xml中所需的值包含在适当的Oasis属性和xsd引用中,然后将其插入到手动帖子请求的标题中是非常简单的。
如果他们没有告诉你,那么棘手的部分就是在Oasis安全标题中确切地知道他们想要什么。 xsd简单地显示了根据Oasis标准可接受的内容,但不一定显示它们正在使用的内容(以及它们的组合)。如果你可以从他们那里得到它,你可以很容易地从Oasis docs中把它们放在一起。
通常,组合是User / Pwd(第一个例子)。如果使用Nonce,通常也会使用Password Digest(密码获取## PasswordDigest而不是## PasswordText),并且是创建的时间和密码的编码哈希(有关更多说明,请参阅Oasis文档)。
但是,我工作的最后一个项目有一个纯文本pwd加上一个没有摘要的nonce。不完全合乎逻辑,因为nonce应该使伪造的请求更加困难,并使用非明文密码,需要由接收器解码并进行比较以验证......所以完全取决于他们想要的服务/使用/需求。
你应该能够将你编制的Oasis标头粘贴到SoapUI中,并很快从他们的响应中快速找出他们可能需要/想要的东西。
如果他们指定了用户/密码(作为文本),您可能会发现其中一个示例(可能是第二个)可行并且不传递现时和创建的元素。
**当使用实际的SOAP方法(Web服务存根而不是手动发布)将Oasis与CF9或更低版本一起使用时,这是Axis 1故障的有趣参考:**
调用Web服务操作时返回的错误是:
AxisFault faultCode: {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}InvalidSecurity faultSubcode: faultString: An error occurred when verifying security for the message. faultActor: faultNode: faultDetail: {http://xml.apache.org/axis/}stackTrace:An error occurred when verifying security for the message. at org.apache.axis.message.SOAPFaultBuilder.createFault(SOAPFaultBuilder.java:221) at org.apache.axis.message.SOAPFaultBuilder.endElement(SOAPFaultBuilder.java:128) at org.apache.axis.encoding.DeserializationContext.endElement(DeserializationContext.java:1087) at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source) at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknown Source) at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source) at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source) at org.apache.xerces.parsers.XML11Conf...
[编辑] 这是一个更多信息,因为我正在处理类似的另一个Web服务。 你的[SOAP] xml可能无法正常工作。我也使用soapUI,除非我在适当的位置添加安全头值,否则服务将返回您正在接收的响应(“InvalidSecurity”),因此不太确定这个xml是您应该尝试复制的内容。我还成功构建了一个简单的基于cf的脚本版本来构建标头,该标头正确生成标头并将其添加到createObject()调用的javastubbed webservice:
doc = xmlNew();
doc['Security'] = XmlElemNew(doc,'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', 'Security');
doc.Security['UsernameToken'] = XmlElemNew(doc, 'UsernameToken');
doc.Security.UsernameToken['Username'] = XmlElemNew(doc, 'Username');
doc.Security.UsernameToken.username.XmlText = "TESTERDUDE" ;
doc.Security.UsernameToken.username.XmlAttributes["xsi:type"] = "xsd:string";
doc.Security.UsernameToken['Password'] = XmlElemNew(doc, 'Password');
doc.Security.UsernameToken.password.XmlText = "YetAnotherPassword";
doc.Security.UsernameToken.password.XmlAttributes["xsi:type"] = "xsd:string";
doc.Security.UsernameToken.password.XmlAttributes["Type"] = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0##PasswordDigest";
doc.Security.UsernameToken['Nonce'] = XmlElemNew(doc, 'Nonce');
doc.Security.UsernameToken.nonce.XmlText = "tKUH8ab3Rokm4t6IAlgcdg9yaEw="; // This would be generated if needed
doc.Security.UsernameToken.nonce.XmlAttributes["xsi:type"] = "xsd:string";
doc.Security.UsernameToken['Created'] = XmlElemNew(doc, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", 'Created');
doc.Security.UsernameToken.created.XmlText = "2010-08-10T10:52:42Z";
doc.Security.UsernameToken.created.XmlAttributes["xsi:type"] = "xsd:string";
addSOAPRequestHeader(ws, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", doc);
这正确地生成[使用getSoapRequest()从服务器端获取xml,它抓取整个xml请求]:
<soapenv:Header>
<Security soapenv:actor="" soapenv:mustUnderstand="0" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<UsernameToken>
<Username xsi:type="xsd:string">TESTERDUDE</Username>
<Password xsi:type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">My Password is my Voice</Password>
<Nonce xsi:type="xsd:string">tKUH8ab3Rokm4t6IAlgcdg9yaEw=</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xsi:type="xsd:string">2010-08-10T10:52:42Z</Created>
</UsernameToken>
</Security>
</soapenv:Header>