使用WCF

时间:2017-01-13 10:42:03

标签: .net wcf saml saml-2.0 federated-identity

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

  • 在代码中创建SAML2令牌
  • 使用WCF,使用SOAP调用Web服务并包含SAML2令牌。
  • 使用下面的代码,我尝试实现上述代码。我有一个在localhost:9000上运行的SOAP Web服务,并且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令牌?

另见

Call WCF service with issued token

1 个答案:

答案 0 :(得分:1)

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

编辑:如果您希望Web服务器重用该标识,则可以使用相同的限制来调用Web服务。考虑一下这些影响,如果Web服务器可以作为中继凭据的代理,这将是一个非常严重的安全警告。这相当于一个中间人攻击。 OAuth2应该可以阻止这类事情。

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