我需要 使用 WIF 保护流式 WCF net.tcp 服务端点 。它应该针对我们的令牌服务器对传入呼叫进行身份验证。该服务是流式传输的,因为它旨在传输大量数据。
这似乎是不可能的。 如果我不能绕开这个问题,我的圣诞节就会毁了,我会在阴沟里喝死,而快乐的购物者会踩到我慢慢冷却的身体。托特严重,你们。
为什么这是不可能的?这是第 22 条军规。
在客户端上,我需要使用从令牌服务器获得的GenericXmlSecurityToken创建一个通道。没问题。
// people around here hate the Framework Design Guidelines. var token = Authentication.Current._Token; var service = base.ChannelFactory.CreateChannelWithIssuedToken(token); return service.Derp();
我说“没问题”吗?问题。事实上,NullReferenceException风格问题。
NullReferenceException
“兄弟,”我问框架,“你还做空检查吗?” 框架是静默的,所以我拆开发现
((IChannel)(object)tChannel). GetProperty<ChannelParameterCollection>(). Add(federatedClientCredentialsParameter);
是异常的来源,并且GetProperty调用正在返回null。那么,WTF?事实证明,如果我打开消息安全并将客户端凭据类型设置为,IssuedToken那么这个属性现在存在于ClientFactory(protip:在 IChannel 中没有“SetProperty”等价物,混蛋)。
GetProperty
null
IssuedToken
ClientFactory
<binding name="OMGWTFLOL22" transferMode="Streamed" > <security mode="Message"> <message clientCredentialType="IssuedToken"/> </security> </binding>
甜的。没有更多的 NRE。然而,现在我的客户 在出生时就有缺陷 (仍然爱他,寿)。深入研究 WCF 诊断(提示:让你最坏的敌人在粉碎他们并将他们赶到你面前,但在享受他们的妇女和孩子的悲痛之前这样做),我认为这是因为服务器和客户端之间的安全不匹配。
“net.tcp://localhost:49627/MyService”不支持请求的升级。这可能是由于不匹配的绑定(例如,在客户端而不是在服务器上启用了安全性)。
检查主机的诊断(再次:迷恋,开车,阅读日志,享受感叹),我看到这是真的
协议类型 application/ssl-tls 被发送到不支持该类型升级的服务。
“好吧,自我,”我说,“我将在主机上打开消息安全!” 我愿意。 如果您想知道它的外观,它是客户端配置的精确副本。抬头。
结果: 卡布姆。
绑定(’NetTcpBinding’、’ http ://tempuri.org/ ‘)支持不能与消息级安全性一起配置的流。考虑选择不同的传输模式或选择传输级别的安全性。
因此, 我的主机不能同时通过令牌进行流式传输和保护 。第 22 条军规。
tl;dr:如何使用 WIF 保护流式 net.tcp WCF 端点?
WCF 在流式传输的一些领域(我在看你, MTOM 1)有一个问题,因为它无法以大多数人认为应该工作的方式执行预身份验证(它只影响对该频道的后续请求) ,而不是第一个请求)好的,所以这不完全是您的问题,但请继续,因为我会在最后解决您的问题。通常 HTTP 质询的工作方式如下:
现在,如果您尝试在服务器上的 WCF 端点上启用 MTOM 流,它不会抱怨。但是,当您在客户端代理上配置它时(正如您应该的那样,它们必须匹配绑定),它会在火热中爆炸。原因是 WCF 试图阻止的上述事件序列是这样的:
请注意,当您只需要发送 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 中的消息安全(参见“缺点”)