提问者:小点点

第22条军规阻止WIF保护的流式TCPWCF服务;破坏我的圣诞节和心理健康


我需要使用WIF保护流式WCF net. tcp服务endpoint。它应该针对我们的令牌服务器验证传入呼叫。该服务是流式传输的,因为它旨在传输大量数据。

这似乎是不可能的。如果我不能绕过这个陷阱,我的圣诞节就会被毁了,我会在排水沟里把自己喝死,而快乐的购物者会跨过我慢慢冷却的身体。太严肃了,伙计们。

为什么这是不可能的?这是第22条军规。

在客户端上,我需要使用我从我们的令牌服务器获得的GenericXmlSecurityToken创建一个通道。没问题。

// people around here hate the Framework Design Guidelines.
var token = Authentication.Current._Token;
var service = base.ChannelFactory.CreateChannelWithIssuedToken(token);
return service.Derp();

我说过“没问题”吗?问题。事实上,NullRecentceException风格的问题。

“兄弟,”我问框架,“你甚至做过空检查吗?”框架是沉默的,所以我拆解并发现

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

是异常的来源,并且GetProperty调用返回null。那么,WTF?事实证明,如果我打开Message Security并将客户端凭据类型设置为IssuedToken,那么这个属性现在存在于ClientFactory中(提示:IChannel中没有“SetProperty”等价物,混蛋)。

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

甜蜜。不再有NRE。然而,现在我的客户在出生时就有缺陷(仍然爱他,尽管)。挖掘WCF诊断(建议:让你最大的敌人在粉碎他们并在你面前驾驶他们之后这样做,但就在享受他们的女人和孩子的哀叹之前),我认为这是因为服务器和客户端之间的安全不匹配。

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

检查主机的图表(再次:粉碎,驾驶,阅读日志,享受哀歌),我看到这是真的

Protocol Type application/ssl-tls已发送到不支持该类型升级的服务。

“嗯,自我,”我说,“我会在主机上打开消息安全性!”我做到了。如果你想知道它是什么样子,它是客户端配置的精确副本。向上看。

结果:轰隆隆。

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

因此,我的主机不能通过令牌进行流式传输和保护。第22条军规。

tl; dr:如何使用WIF保护流式net.tcp WCFendpoint???


共1个答案

匿名用户

WCF在流媒体的一些领域有问题(我在看着你,MTOM1),因为它无法像大多数人认为的那样执行预认证(它只影响对该通道的后续请求,而不是第一个请求)好的,所以这不完全是你的问题,但请继续关注,因为我最后会谈到你的问题。通常HTTP挑战是这样的:

  1. 客户端匿名访问服务器
  2. 服务器说,对不起,401,我需要身份验证
  3. 客户端使用身份验证令牌访问服务器
  4. 服务器接受。

现在,如果您尝试在服务器上的WCFendpoint上启用MTOM流,它不会抱怨。但是,当您在客户端代理上配置它时(正如您应该做的那样,它们必须匹配绑定),它会爆炸式地死亡。原因是WCF试图阻止的上述事件序列是这样的:

  1. 客户端在单个POST中将100MB文件匿名流式传输到服务器
  2. 服务器说对不起,401,我需要身份验证
  3. 客户端再次使用身份验证标头将100MB文件流式传输到服务器
  4. 服务器接受。

请注意,当您只需要发送100MB时,您刚刚向服务器发送了200MB。这就是问题所在。答案是在第一次尝试时发送身份验证,但这在WCF中是不可能的,除非编写自定义行为。无论如何,我跑题了。

您的问题

首先,让我告诉你,你正在尝试的是不可能的。现在,为了让你停止旋转,让我告诉你为什么:

令我震惊的是,您现在正徘徊在类似的问题类别中。如果您启用消息级安全性,客户端必须将整个数据流加载到内存中,然后才能使用ws-security所需的通常哈希函数和xml签名实际关闭消息。如果它必须读取整个流来签署单个消息(这不是真正的消息,但它是单个连续流),那么您可以在这里看到问题。WCF将不得不在“本地”流式传输一次以计算消息安全性,然后再次流式传输以将其发送到服务器。这显然是一件愚蠢的事情,因此WCF不允许流数据的消息级安全性。

因此,这里简单的回答是,您应该将令牌作为参数发送到初始Web服务,或者作为SOAP标头,并使用自定义行为来验证它。您不能使用WS安全性来做到这一点。坦率地说,这不仅仅是一个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中的消息安全性(参见“缺点”)