2010-11-02 173 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(); 
}