使用WCF调用带有SAML2令牌的SOAP web服务



我正在尝试执行以下操作:

  • 在代码中创建SAML2令牌
  • 使用WCF,使用SOAP调用web服务并包含SAML2令牌
  • 使用下面的代码,我尝试实现上面的内容。我有一个在localhost:9000上运行的SOAPWeb服务,Fiddler正在运行以检查流量
  • 现在,当我运行测试客户端时,我在var response = channel.CrossGatewayQuery()服务调用上得到一个异常(整个堆栈跟踪在问题的末尾)

异常:

SecurityNegotiationException
Inner exception: Fault Exception
The message with Action 'http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue' cannot be processed at the receiver, 
due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions 
between sender and receiver) or a binding/security mismatch between the sender and the receiver.  Check that sender and receiver 
have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

Fiddler记录以下请求:

POST /RespondingGateway/ HTTP/1.1
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:MessageID>urn:uuid:3609d530-4b8d-4e9c-8907-bb346cfe0c91</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://localhost:9000/RespondingGateway/</a:To>
</s:Header>
<s:Body>
<t:RequestSecurityToken Context="uuid-e6928b4c-6100-4a1d-8818-8e7436f7a935-12" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</t:TokenType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:KeySize>256</t:KeySize>
<t:BinaryExchange ValueType=" http://schemas.xmlsoap.org/ws/2005/02/trust/tlsnego" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">...</t:BinaryExchange>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>

因此,在我看来,WCF似乎正在尝试联系STS来发布令牌。对的

我不希望WCF从STS获得令牌,我想提供我自己的令牌。

问题/问题:

  • 我做错了什么
  • 我用错装订了吗?绑定配置是否错误
  • SAML2令牌的(创建)看起来正常吗

以下是我用于测试的代码:

void SendRequest(XElement requestBody)
{
var binding = new WSFederationHttpBinding();
binding.Security.Mode = WSFederationHttpSecurityMode.Message;
binding.Security.Message.IssuedTokenType = 
"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
var remoteAddress = 
new EndpointAddress("http://localhost:9000/RespondingGateway/");
var channelFactory = 
new ChannelFactory<ICrossGatewayQueryITI38>(binding, remoteAddress);
// if true I get a "Cardspace not installed" exception
channelFactory.Credentials.SupportInteractive = false; 
var channel = channelFactory.CreateChannelWithIssuedToken(
CreateSaml2Token(
GetCertificate(
"thumbprint_of_certificate_i_have_private_key_of"
)
)
);
var response = channel.CrossGatewayQuery(CreateRequestMessage(requestBody));
var body = XElement.ReadFrom(response.GetReaderAtBodyContents());
Console.WriteLine(body.ToString());
}
Saml2SecurityToken CreateSaml2Token(X509Certificate2 certificate)
{
var std = new SecurityTokenDescriptor();
std.TokenIssuerName = "Foobar";
std.TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
std.Lifetime = new Lifetime(DateTime.Now, DateTime.Now + TimeSpan.FromDays(10));
std.Subject = new ClaimsIdentity(new[] { new Claim("User", "TheUserName") });
if (certificate != null)
{
var ski = new SecurityKeyIdentifier(new SecurityKeyIdentifierClause[] { new X509SecurityToken(certificate).CreateKeyIdentifierClause<X509SubjectKeyIdentifierClause>() });
std.SigningCredentials = new X509SigningCredentials(certificate, ski); ;
}
return (Saml2SecurityToken)new Saml2SecurityTokenHandler().CreateToken(std);
}
Message CreateRequestMessage(XElement requestBody)
{
return Message.CreateMessage(
MessageVersion.Soap12WSAddressing10,
"urn:ihe:iti:2007:CrossGatewayQuery",
requestBody
);
}
[ServiceContract(Namespace = "urn:ihe:iti:xds-b:2007")]
public interface ICrossGatewayQueryITI38
{
[OperationContract(Action = "urn:ihe:iti:2007:CrossGatewayQuery", ReplyAction = "urn:ihe:iti:2007:CrossGatewayQueryResponse")]
Message CrossGatewayQuery(Message request);
}

这是堆栈跟踪:

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.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.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
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 XDSDotNet.ICrossGatewayQueryITI38.CrossGatewayQuery(Message request)
at UserQuery.SendRequest(XElement requestBody)
at UserQuery.Main()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

tgolisch评论后更新

忘记在代码中创建SAML令牌的部分。

用户进行身份验证并从身份验证提供者接收SAML令牌。稍后,用户访问URL,web应用程序需要对服务进行SOAP调用,以获取用户需要的数据。

因此,让我们在本文中重新表述这个问题:web应用程序(使用WCF)如何对web服务进行SOAP调用,并包含用户登录时提供的SAML令牌?

另请参阅

使用已颁发的令牌调用WCF服务

您试图做的事情从根本上违背了联邦身份的概念。如果你组装了自己的令牌,那么它看起来就像是伪造的令牌,因为它确实是伪造的。令牌需要来自身份验证服务器,并(因此)根据身份验证服务器(或联盟中的成员服务器)进行检查。

编辑:如果您希望web服务器在调用web服务时重用该标识,则同样的限制也适用。考虑一下影响,如果web服务器可以充当中继凭据的代理,这将是一个非常严重的安全警告。这将相当于一个中间人的攻击。OAuth2应该阻止这种事情的发生。

因此,解决方案是编写应用程序,以便客户端机器(已通过身份验证)可以直接联系web服务。从架构和设计的角度来看,这是一个麻烦,但这就是工作方式。这可能解释了JS框架(JQuery、Angular等)最近的流行

最新更新