我需要使用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???
WCF在流媒体的一些领域有问题(我在看着你,MTOM1),因为它无法像大多数人认为的那样执行预认证(它只影响对该通道的后续请求,而不是第一个请求)好的,所以这不完全是你的问题,但请继续关注,因为我最后会谈到你的问题。通常HTTP挑战是这样的:
现在,如果您尝试在服务器上的WCFendpoint上启用MTOM流,它不会抱怨。但是,当您在客户端代理上配置它时(正如您应该做的那样,它们必须匹配绑定),它会爆炸式地死亡。原因是WCF试图阻止的上述事件序列是这样的:
请注意,当您只需要发送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中的消息安全性(参见“缺点”)