2010-11-02 177 views
4

我正在使用WCF访问Web服务。使用WSHttpBinding,将安全模式设置为Transport(https),并且客户端凭证类型为Basic。当我尝试使用代理访问服务时,获取401未经授权的例外。使用WCF的Http请求中缺少授权标头

这里是绑定

var binding = new WSHttpBinding() 
     { 
      UseDefaultWebProxy = true, 
      Security = 
      { 
       Mode = SecurityMode.Transport, 
       Transport = 
       { 
        ClientCredentialType = HttpClientCredentialType.Basic, 
       }, 
      } 
     }; 

这里是服务呼叫

var client = new InternetClient(binding, new EndpointAddress("httpsurl")); 

     client.ClientCredentials.UserName.UserName = "username"; 
     client.ClientCredentials.UserName.Password = "password"; 
     client.ProcessMessage("somevalue"); 

当看着使用HTTP分析 CONNECT报头

(Request-Line中)的HTTP头:CONNECT,某.com:443 HTTP/1.1
Host:somehost.com
代理连接:保持活动

POST HEADER

(请求线):POST /Company/1.0 HTTP/1.1
内容类型:应用/肥皂+ xml的;字符集= UTF-8
VsDebuggerCausalityData:uIDPo + voStemjalOv5LtRotFQ7UAAAAAUKLJpa755k6oRwto14BnuE2PDtYKxr9LhfqXFSOo8pEACQAA
主持人:somehost.com
的Content-Length:898
期望:100继续
连接:保持活动

如果你看到标题授权标头丢失

现在我的问题是为什么WCF调用缺少Authorization标头?我错过了什么吗? 。请询问您是否需要更多信息

回答

0

其实,我对这个问题错了。运行HTTP分析器时,我确实看到了不同的行为。当Http anaylzer运行时,我的应用程序在收到401响应后崩溃。当Http分析器应用程序关闭时,上面的代码按预期工作。

8

这是一个常见问题,但情况与您的想法不同。

事实证明,最初对于第一个请求,配置为使用HTTP基本身份验证的WCF客户端仍然会向服务器发送请求而不需要必要的授权标头。这是WCF客户端使用的HttpWebRequest类的默认行为。

通常,web服务服务器将随后返回到WCF客户端,在其上,后者将重发该消息授权报头中的HTTP 401未授权响应。这意味着在HTTP基本身份验证的正常情况下,服务器往往会有一次无用的往返。

这也解释了为什么您的嗅探消息中缺少标题。一些Http sniffs可能不会传递401响应,所以整个交换都会搞砸。

通过在每个请求中手动注入所需的授权标头,可以避免服务器往返和对401响应的依赖。见例如how to manually inject Authorization header into WCF request

+0

什么?你确定这种行为吗?这对我来说似乎很荒谬。 – 2013-08-29 12:02:44

+0

是的,绝对肯定 - 广泛测试它,并使用Fiddler研究行为 - 推荐的解决方案如上所述工作。我已经在生产中运行了。 – whale70 2013-09-06 19:13:29

+0

是的,它也适用于我。但我仍然困惑。对于WCF开发人员创建身份验证机制,如果他们不是从盒子开始工作,我们必须自定义该行为?! – 2013-09-08 19:52:51

0

请注意Expect:100-在标题中继续。这就是往返的原因。

在你的web.config将这个再试一次:

<system.net> 
    <settings> 
     <servicePointManager expect100Continue="false"/> 
    </settings> 
</system.net> 
5

作为一个轻微的修改从以前的答案,支持异步/等待来电,实际上就可以创建一个新的OperationContext并围绕它传递不管你喜欢什么线程(只要它不是并行线程共享,因为它不是线程安全对象)

var client = new MyClient(); 
client.ClientCredentials.UserName.UserName = "username"; 
client.ClientCredentials.UserName.Password = "password"; 
var httpRequestProperty = new HttpRequestMessageProperty(); 
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(client.ClientCredentials.UserName.UserName + ":" + client.ClientCredentials.UserName.Password)); 

var context = new OperationContext(ormClient.InnerChannel); 
using (new OperationContextScope(context)) 
{ 
    context.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty; 
    return await client.SomeMethod(); 
}