我需要使用WIF保护流式WCF net.tcp服务端点.它应该对我们的令牌服务器验证传入呼叫.该服务是流式传输的,因为它旨在传输大量数据.
这似乎是不可能的. 如果我无法绕过捕获物,我的圣诞节将被毁掉,我会在阴沟里喝水,而快乐的购物者则会越过我缓慢冷却的身体.手提箱严重,你们.
为什么这不可能?这是Catch-22.
在客户端上,我需要使用我从令牌服务器获取的GenericXmlSecurityToken创建一个通道.没问题.
// people around here hate the Framework Design Guidelines. var token = Authentication.Current._Token; var service = base.ChannelFactory.CreateChannelWithIssuedToken(token); return service.Derp();
我说"没问题"吗?Problemo.事实上,NullReferenceException
风格问题.
"兄弟,"我问框架,"你甚至无效检查吗?" 框架是沉默的,所以我拆开并发现了
((IChannel)(object)tChannel). GetProperty(). Add(federatedClientCredentialsParameter);
是异常的来源,并且GetProperty
呼叫正在返回null
.那么,WTF?事实证明,如果我打开Message安全性并将客户端凭据类型设置为,IssuedToken
那么此属性现在存在于ClientFactory
(protip:IChannel中没有"SetProperty"等效,即混蛋).
甜.没有更多的NRE.然而,现在我的客户在出生时出现了故障(仍然爱他,所以).通过WCF诊断进行挖掘(protip:让你的最坏的敌人在击碎它们并在你面前驾驶它们之前做到这一点,但在享受他们的女人和孩子的悲伤之前),我看到它是因为服务器和客户端之间的安全性不匹配.
'net.tcp:// localhost:49627/MyService'不支持请求的升级.这可能是由于绑定不匹配(例如在客户端而不是在服务器上启用安全性).
检查主机的诊断(再次:压碎,驱动,读取日志,享受哀叹),我看到这是真的
协议类型application/ssl-tls已发送到不支持该类型升级的服务.
"好吧,自我,"我说,"我只是打开主机上的Message安全性!" 我做到了. 如果你想知道它的样子,它就是客户端配置的精确副本.抬头.
结果: Kaboom.
绑定('NetTcpBinding',' http://tempuri.org/ ')支持不能与消息级安全性一起配置的流.考虑选择不同的传输模式或选择传输级别安全性.
因此,我的主机不能通过令牌进行流式传输和保护.第二十二条军规.
tl; dr:如何使用WIF保护流式net.tcp WCF端点???
WCF在一些领域有流媒体(我正在看你,MTOM 1),因为它无法按照大多数人认为应该工作的方式执行预身份验证的基本问题(它只会影响该频道的后续请求) ,不是第一个请求)好的,所以这不是你的问题,但请跟进,因为我会在最后找到你的.通常,HTTP挑战的工作方式如下:
客户端匿名访问服务器
服务器说,抱歉,401,我需要身份验证
客户端使用身份验证令牌命中服务器
服务器接受.
现在,如果您尝试在服务器上的WCF端点上启用MTOM流,则不会抱怨.但是,当你在客户端代理上配置它时(你应该,它们必须匹配绑定)它会在火热的死亡中爆炸.原因是WCF试图阻止的上述事件序列是这样的:
客户端在一个POST中匿名将100MB文件流式传输到服务器
服务器说抱歉,401,我需要身份验证
客户端再次使用身份验证标头将100MB文件流式传输到服
服务器接受.
请注意,当您只需要发送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中的消息安全性(参见"缺点".)