Catch-22阻止使用WIF保护流式传输的TCP WCF服务;损害了我的圣诞节和心理健康。

184
我有一个需求,需要使用WIF来保护流式传输的WCF net.tcp服务端点。它应该对传入的调用进行身份验证并与我们的令牌服务器进行通信。该服务是流式的,因为它旨在传输大量数据等内容。
这似乎是不可能的。如果我无法解决这个问题,我的圣诞节将被毁掉,我会喝醉自己死在街头,而欢乐的购物者会跨过我的渐渐冷却的身体。这是完全认真的。
为什么这是不可能的?这就是困境。
在客户端,我需要使用从我们的令牌服务器获得的GenericXmlSecurityToken创建一个通道。没有问题。
// people around here hate the Framework Design Guidelines.
var token = Authentication.Current._Token;
var service = base.ChannelFactory.CreateChannelWithIssuedToken(token);
return service.Derp();

我说过“没问题”吗?有问题。事实上,是NullReferenceException类型的问题。

“兄弟,”我问框架,“你甚至进行了空值检查吗?”框架保持沉默,所以我进行了反汇编并发现

((IChannel)(object)tChannel).
    GetProperty<ChannelParameterCollection>().
    Add(federatedClientCredentialsParameter);

异常的源头是GetProperty调用返回了null。那么问题出在哪里呢?原来,如果我开启消息安全性并将客户端凭据类型设置为IssuedToken,那么这个属性现在存在于ClientFactory中(专业提示:在IChannel中没有“SetProperty”等效方法)。

<binding name="OMGWTFLOL22" transferMode="Streamed" >
    <security mode="Message">
        <message clientCredentialType="IssuedToken"/>
    </security>
</binding>

甜蜜。不再有NREs了。然而,现在我的客户端在出生时就出问题了(尽管我仍然爱他)。通过WCF诊断(小技巧:在击败敌人、驱逐他们之前,让他们这样做,但在享受他们的妇女和孩子的悲痛之前),我看到这是由于服务器和客户端之间的安全不匹配造成的。

所请求的升级不受'net.tcp://localhost:49627/MyService'支持。这可能是由于绑定不匹配(例如客户端启用了安全性,而服务器没有启用)。

检查主机的诊断信息(再次:击败、驱赶、读取日志、享受哀叹),我发现这是真的。

发送给不支持该类型升级的服务的协议类型为application/ssl-tls。

“好吧,自己,”我说,“我只需在主机上打开消息安全性!” 我做到了。如果您想知道它是什么样子,它与客户端配置完全相同。向上看。
结果:砰。

绑定('NetTcpBinding','http://tempuri.org/')支持流式传输,无法与消息级安全性一起配置。考虑选择不同的传输模式或选择传输级别安全。

因此,我的主机不能同时通过令牌进行流式处理和安全保护。进退两难。

简而言之:如何使用WIF保护经过流式处理的net.tcp WCF端点?


3
好的,可能是一个愚蠢的问题,但是WIF是否真的需要消息模式?传输模式听起来似乎更适合流式处理,例如未经测试的 <security mode="Transport" /><transport clientCredentialType="IssuedToken" /></security> - Joachim Isaksson
3
TransportWithMessageCredential 模式可能是另一个选择。 - Joachim Isaksson
3
TMLK,MessageSecurity可以对缓冲有效载荷进行签名和加密,但在处理流时会出现问题。您是否考虑使用authenticationMode = IssuedTokenOverTransport? - OnoSendai
7
让我看看能否召唤一些鬼魂帮助挽救你的假期。这里有一些提示:http://social.msdn.microsoft.com/Forums/vstudio/en-US/eacae126-7539-4de4-81fe-4a1f923a3482/federation-over-tcp-part-2 - OnoSendai
2
你能否分享一个测试用例项目,让其他人可以进行实验吗? - antiduh
显示剩余14条评论
1个回答

44

在一些流式传输的方面,WCF存在一些需要注意的地方(我在看着你,MTOM1),由于它在执行预身份验证时存在一个基本问题,与大多数人认为应该进行的方式不同(仅会影响该通道的后续请求,而不是第一个请求)。好吧,这实际上并不是你的问题,但请跟随我的思路,最终我会解决你的问题。通常HTTP挑战工作方式如下:

  1. 客户端匿名连接服务器
  2. 服务器说,抱歉,401,我需要验证
  3. 客户端使用身份验证令牌连接服务器
  4. 服务器接受。

现在,如果您尝试在服务器上启用WCF端点上的MTOM流式传输,它将不会抱怨。但是,当您在客户端代理上配置它(因为它们必须匹配绑定)时,它将出现灾难性错误。原因是WCF试图防止的事件序列是:

  1. 客户端以匿名方式向服务器发送100MB文件
  2. 服务器说抱歉,401,我需要验证
  3. 客户端再次使用带有身份验证标头的100MB文件向服务器发送流
  4. 服务器接受。

请注意,您只需要发送100MB,而您实际上却向服务器发送了200MB。那么,这就是问题所在。答案是在第一次尝试时发送身份验证,但在WCF中没有这个可能,除非编写自定义行为。无论如何,我偏离了话题。

你的问题

首先,让我告诉你,你正在尝试的是不可能的2。现在为了让你不再转圈子,让我告诉你原因:

我觉得你现在遇到了类似的问题。如果启用消息级安全性,则客户端必须将整个数据流加载到内存中,然后才能使用ws-security所需的常规哈希函数和xml签名关闭消息。如果必须读取整个流以签署单个消息(实际上它不是一条消息,而是一个单一连续流),那么问题就出现在这里。WCF必须在“本地”流传一次以计算消息安全性,然后再次流传以将其发送到服务器。显然,这很愚蠢,因此WCF不允许流式传输数据使用消息级安全性。

因此,简单的答案是,您应该将令牌作为初始Web服务的参数或作为SOAP标头发送,并使用自定义行为验证它。您不能使用WS-Security来完成此操作。坦率地说,这不仅是WCF的问题-我无法看出它如何在任何其他堆栈中实际上起作用。

解决MTOM问题

这只是一个示例,说明我如何解决基本身份验证的MTOM流问题,因此您可以将其核心用于解决您的问题。关键在于要启用自定义消息检查器,必须在客户端代理上禁用所有安全性概念(仍然启用于服务器),除了传输级别(SSL):

this._contentService.Endpoint.Behaviors.Add(
    new BasicAuthenticationBehavior(
        username: this.Settings.HttpUser,
        password: this.Settings.HttpPass));
var binding = (BasicHttpBinding)this._contentService.Endpoint.Binding;
binding.Security.Mode = BasicHttpSecurityMode.Transport; // SSL only            
binding.Security.Transport.ClientCredentialType = 
   HttpClientCredentialType.None; // Do not provide

请注意,我在这里关闭了传输安全,因为我将使用消息检查器和自定义行为来提供传输安全:

internal class BasicAuthenticationBehavior : IEndpointBehavior
{
    private readonly string _username;
    private readonly string _password;

    public BasicAuthenticationBehavior(string username, string password)
    {
        this._username = username;
        this._password = password;
    }
    public void AddBindingParameters(ServiceEndpoint endpoint, 
        BindingParameterCollection bindingParameters) { }
    public void ApplyClientBehavior(ServiceEndpoint endpoint,
        ClientRuntime clientRuntime)
    {
        var inspector = new BasicAuthenticationInspector(
            this._username, this._password);
        clientRuntime.MessageInspectors.Add(inspector);
    }
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
        EndpointDispatcher endpointDispatcher) { }
    public void Validate(ServiceEndpoint endpoint) { }
}

internal class BasicAuthenticationInspector : IClientMessageInspector
{
    private readonly string _username;
    private readonly string _password;

    public BasicAuthenticationInspector(string username, string password)
    {
        this._username = username;
        this._password = password;
    }

    public void AfterReceiveReply(ref Message reply,
        object correlationState) { }

    public object BeforeSendRequest(ref Message request,
        IClientChannel channel)
    {
        // we add the headers manually rather than using credentials 
        // due to proxying issues, and with the 101-continue http verb 
        var authInfo = Convert.ToBase64String(
            Encoding.Default.GetBytes(this._username + ":" + this._password));

        var messageProperty = new HttpRequestMessageProperty();
        messageProperty.Headers.Add("Authorization", "Basic " + authInfo);
        request.Properties[HttpRequestMessageProperty.Name] = messageProperty;

        return null;
    }
}

所以,这个例子适用于任何遇到MTOM问题的人,同时也为您提供一个类似实现身份验证由主WIF安全令牌服务生成的令牌的框架。

希望能对你有所帮助。

(1) 大数据和流式传输

(2) WCF中的消息安全(参见“缺点”部分)。


MTOM和基本授权,以及MTOM和OAuth2? - Kiquenet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接