问题
Everything I've found so far says I should be able to use WCF to call a .asmx web service that uses WS-Security. The question is how to configure the WCF-Port. I'm using WCF-BasicHttp. First of all, is that okay? Second, how to enter the user/pass properly. On the security tab, which "Security Mode" should I pick?
The only one that seems to let me enter credentials is TransportWithMessageCredential, then I can click the "Edit" button by username credentials and enter a user/pass.
But when I did, I got this:
<soap:Fault xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<faultcode xmlns:q0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">q0:Security</faultcode>
<faultstring>Microsoft.Web.Services3.Security.SecurityFault: Security requirements are not satisfied because the security header is not present in the incoming message.
at Microsoft.Web.Services3.Design.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity(SoapEnvelope envelope, Security security)
at MSB.RCTExpress.Presentation.Web.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity(SoapEnvelope envelope, Security security)
in C:\projects\la1safe1\RCT Express\MSB.RCTExpress\3.10\Presentation.Web\UsernameOverTransportNoSendNone.cs:line 27
at Microsoft.Web.Services3.Security.ReceiveSecurityFilter.ProcessMessage(SoapEnvelope envelope)
at Microsoft.Web.Services3.Pipeline.ProcessInputMessage(SoapEnvelope envelope)
at Microsoft.Web.Services3.WseProtocol.FilterRequest(SoapEnvelope requestEnvelope)
at Microsoft.Web.Services3.WseProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)</faultstring>
<faultactor>http://rct3.msbexpress.net/demo/ExpressLync/ValuationService.asmx</faultactor>
</soap:Fault>
Any ideas?
Thanks,
Neal Walters
Follow-up to TomasR's post - using WS-HTTP binding:
1) BizTalk "Consume WCF Wizard" builds a custom binding file and a WS-BasicHTTP Binding file, so I changed SendPort, and manually copied over all the configurations.
Set as follows:
Security Mode: Message
Message Client Credential Type: UseName
Algorithm Suite: Basic256 [I had no idea what to put here]
I also checked two other boxes:
a) Negotiate service credential [if I don't check this, it wants a "thumbprint"]
b) Establish security context [also tried not checking this one]
2) Ran and got this error:
Description:
The adapter failed to transmit message going to send port "WcfSendPort_ValuationServicePort_ValuationServicePortSoap" with URL "http://rct3.msbexpress.net/demo/ExpressLync/ValuationService.asmx". It will be retransmitted after the retry interval specified for this Send Port.
Details:"System.NullReferenceException: Object reference not set to an instance of an object.
Server stack trace:
at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
at System.ServiceModel.Security.SspiNegotiationTokenProvider.OnOpen(TimeSpan timeout)
at System.ServiceModel.Security.TlsnegoTokenProvider.OnOpen(TimeSpan timeout)
at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Security.CommunicationObjectSecurityTokenProvider.Open(TimeSpan timeout)
at System.ServiceModel.Security.SecurityUtils.OpenTokenProviderIfRequired(SecurityTokenProvider tokenProvider, TimeSpan timeout)
at System.ServiceModel.Security.SymmetricSecurityProtocol.OnOpen(TimeSpan timeout)
at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation,
EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open()
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System.ServiceModel.ICommunicationObject.Open()
at Microsoft.BizTalk.Adapter.Wcf.Runtime.WcfClient`2.GetChannel[TChannel](IBaseMessage bizTalkMessage,
ChannelFactory`1& cachedFactory)
at Microsoft.BizTalk.Adapter.Wcf.Runtime.WcfClient`2.SendMessage(IBaseMessage bizTalkMessage)".
Now tried custom binding, added user/pass and get this error:
<soap:Fault xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Code>
<soap:Value xmlns:q0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">q0:Security</soap:Value>
</soap:Code>
<soap:Reason>
<soap:Text xml:lang="en">Microsoft.Web.Services3.Security.SecurityFault:
Security requirements are not satisfied because the security header is not present in the incoming message.
at Microsoft.Web.Services3.Design.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity(SoapEnvelope envelope,
Security security)
at MSB.RCTExpress.Presentation.Web.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity
(SoapEnvelope envelope, Security security) in
C:\projects\la1safe1\RCT Express\MSB.RCTExpress\3.10\Presentation.Web\UsernameOverTransportNoSendNone.cs:line 27
at Microsoft.Web.Services3.Security.ReceiveSecurityFilter.ProcessMessage(SoapEnvelope envelope)
at Microsoft.Web.Services3.Pipeline.ProcessInputMessage(SoapEnvelope envelope)
at Microsoft.Web.Services3.WseProtocol.FilterRequest(SoapEnvelope requestEnvelope)
at Microsoft.Web.Services3.WseProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)</soap:Text>
</soap:Reason>
<soap:Node>http://rct3.msbexpress.net/demo/ExpressLync/ValuationService.asmx</soap:Node>
</soap:Fault>
My next attempt, went back to WS-HTTP, but tried to put the User/Pass in a message assignment rather than in the SendPort:
msgRCTGetRequest(SOAP.Username) = "myuser";
msgRCTGetRequest(SOAP.Password) = "mypass";
//msgRCTGetRequest(SOAP.UseSoap12) = true;
Resulted in this error:
<soap:Fault xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Code>
<soap:Value>soap:Sender</soap:Value>
</soap:Code><soap:Reason>
<soap:Text xml:lang="en">System.Web.Services.Protocols.SoapHeaderException: WSE012: The input was not a valid SOAP message because the following information is missing: action.
at Microsoft.Web.Services3.Utilities.AspNetHelper.SetDefaultAddressingProperties(SoapContext context, HttpContext httpContext)
at Microsoft.Web.Services3.WseProtocol.CreateRequestSoapContext(SoapEnvelope requestEnvelope)
at Microsoft.Web.Services3.WseProtocol.FilterRequest(SoapEnvelope requestEnvelope)
at Microsoft.Web.Services3.WseProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)</soap:Text>
</soap:Reason>
</soap:Fault>
Fifth attempt, about to give up and open a Microsoft ticket:
msgRCTGetRequest(WCF.UserName) = "myuser";
msgRCTGetRequest(WCF.Password) = "mypass";
msgRCTGetRequest(WCF.Action) = "GetPropertyInfoSourceRecordPolicyNum";
msgRCTGetRequest(SOAP.MethodName) = "GetPropertyInfoSourceRecordPolicyNum";
msgRCTGetRequest(SOAP.Username) = "myuser";
msgRCTGetRequest(SOAP.Password) = "mypass";
same error as fourth attempt.
According to the doc of the vendor providing the web service, I should put the user in W-Security UsernameToken element, the password in WS-Security password, and set the element's attribute to "PasswordDigest". It also says "This token should be added to the SOAP request for the Web method." I'm not sure how this translates from the old WSE3 days to the new WCF days.
回答1:
Neal, for WS-Security, you need to use the WCF-WsHttp binding/Adapter. WCF-BasicHttp is only for the simpler scenarios where the WS-* protocols are not needed.
回答2:
.NET 4.0 and .NET 3.5 SP1 with hotfix 971831 allow WS-Security over http transport. Try using this sample binding:
<customBinding>
<binding name="httpAndWSSecurity">
<security authenticationMode="UserNameOverTransport"
allowInsecureTransport="true"/>
<textMessageEncoding messageVersion="Soap11WSAddressingAugust2004" />
<httpTransport/>
</binding>
Also see this MSDN article on SecurityBindingElement.AllowInsecureTransport
回答3:
Use custom binding, and from the BizTalk send port, click configure, then go to the right-most tab which says "Import/Export". Paste the following XML into a file (sample.config) and then import it into the configuration port. This basically saves the time of manually typing a lot of stuff on the binding tab.
<configuration>
<system.serviceModel>
<client>
<endpoint
address="http://rct3.msbexpress.net/demo/ExpressLync/ValuationService.asmx"
binding="customBinding"
bindingConfiguration="ValuationServicePortSoap12"
contract="BizTalk"
name="WcfSendPort_ValuationServicePort_ValuationServicePortSoap12"/>
</client>
<bindings>
<customBinding>
<binding name="ValuationServicePortSoap12">
<security authenticationMode="UserNameOverTransport"
messageProtectionOrder="SignBeforeEncrypt"
includeTimestamp="true"
messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
requireDerivedKeys="false"
requireSignatureConfirmation="false"/>
<textMessageEncoding messageVersion="Soap11WSAddressingAugust2004"/>
<httpsTransport />
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>
The above is not very intuitive (I'm still waiting for a link from Microsoft which describes this is more detail).
Then you still specify the user/pass on the credentials tab.
However, this caused a problem for us, in that the vendor's .asmx web service we were calling did not have IIS set to "requires SSL". Apparently, that is a requirement for this to work with WCF. In other words, it works fine with WSE3 calling .NET to .NET, but when trying to call WCF to .asmx, WCF has a slightly more stringent requirement.
Neal Walters
来源:https://stackoverflow.com/questions/1668950/biztalk-sendport-wcf-calling-asmx-web-service-using-ws-security