2014-06-07 82 views
11

我在使用clientCredentialType="UserName"連接到我的WCF服務時遇到問題。WCF身份驗證 - 驗證郵件安全性時出錯

當我運行下面的代碼,我得到一個錯誤

的FaultException:該消息確認安全時發生錯誤。

當玩弄一些綁定值時,我也得到Access is denied.

Fiddler說沒有授權頭,我也找不到請求中的用戶名或密碼。

下面是我的配置摘錄:

<system.webServer> 
    <modules runAllManagedModulesForAllRequests="true"/> 
    </system.webServer> 
    <services> 
     <service name="InventoryServices.MobileAPI" behaviorConfiguration="customBehaviour"> 
     <endpoint address="" 
        binding="basicHttpBinding" 
        bindingConfiguration="secureHttpBinding" 
        contract="InventoryServices.IMobileAPI"/> 

     <endpoint address="mex" 
        binding="mexHttpsBinding" 
        contract="IMetadataExchange" /> 
     </service> 
    </services> 
    <behaviors> 
     <serviceBehaviors> 
     <behavior name="customBehaviour"> 
      <serviceSecurityAudit auditLogLocation="Application" serviceAuthorizationAuditLevel="Failure" messageAuthenticationAuditLevel="Failure" suppressAuditFailure="true" /> 
      <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> 
      <serviceMetadata httpsGetEnabled="true"/> 
      <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> 
      <serviceDebug includeExceptionDetailInFaults="true"/> 
      <serviceCredentials> 
      <userNameAuthentication userNamePasswordValidationMode="Custom" 
       customUserNamePasswordValidatorType="InventoryLibrary.Helpers.UserAuthentication,InventoryLibrary"/> 
      </serviceCredentials> 
     </behavior> 
     </serviceBehaviors> 
    </behaviors> 
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> 
    <bindings> 
     <basicHttpBinding> 
     <binding name="secureHttpBinding"> 
      <security mode="TransportWithMessageCredential"> 
      <transport clientCredentialType="Basic" proxyCredentialType="Basic" realm="MyRealm"/> 
      <message clientCredentialType="UserName" algorithmSuite="Default" /> 
      </security> 
     </binding> 
     </basicHttpBinding> 
    </bindings> 

我的用戶名/密碼驗證程序看起來像這樣:

public class UserAuthentication : UserNamePasswordValidator { 
     public override void Validate(string userName, string password) { 

      EntitiesContext db = new EntitiesContext(); 
      db.Logs.Add(new DomainModels.Log() { 
       DateLogged = DateTime.Now, 
       Message = "hit auth", 
       Type = DomainModels.LogType.Info 
      }); 
      db.SaveChanges(); 

      try { 

       if (userName == "test" && password == "test123") { 
        Console.WriteLine("Authentic User"); 
       } 
      } 
      catch (Exception ex) { 
       throw new FaultException("Unknown Username or Incorrect Password"); 
      } 
     } 
    } 

我這是對我的服務進行簡單的測試:

[OperationContract] 
[XmlSerializerFormat] 
void Test(); 

[PrincipalPermission(SecurityAction.Demand, Name = "test")] 
public void Test() { 

} 

我在我的服務器上有一個自簽名的SSL證書,我可以訪問我的服務/元數據。

然後我加入了一個控制檯應用程序的服務引用,並嘗試用下面這段代碼連接到服務:

class Program { 
    static void Main(string[] args) { 

     Stuff.InitiateSSLTrust(); 

     BasicHttpBinding binding = new BasicHttpBinding(); 
     binding.Security.Mode = BasicHttpSecurityMode.Transport; 
     binding.Security.Transport.Realm = "MyRealm"; 

     ServiceReference1.MobileAPIClient serviceProxy = new ServiceReference1.MobileAPIClient(binding, new EndpointAddress("https://xx.xx.xx.xx/InventoryServices.MobileApi.svc")); 

     serviceProxy.ClientCredentials.UserName.UserName = "test"; 
     serviceProxy.ClientCredentials.UserName.Password = "test123"; 

     try { 

      var a = serviceProxy.Login("a", "b"); 
     } 
     catch (Exception ex) { 
      var ex2 = ex; 
     } 
    } 
} 

public class Stuff { 
    public static void InitiateSSLTrust() { 
     try { 
      //Change SSL checks so that all checks pass 
      ServicePointManager.ServerCertificateValidationCallback = 
       new RemoteCertificateValidationCallback(
        delegate { return true; } 
       ); 
     } 
     catch (Exception ex) { 
     } 
    } 
} 

我檢查事件查看器在服務器上,並出現此錯誤每個請求:

MessageSecurityException:安全處理器無法在消息中找到安全標頭。這可能是因爲該消息是不安全的錯誤,或者是因爲通信雙方之間存在綁定不匹配。如果服務配置了安全性並且客戶端沒有使用安全性,則會發生這種情況。

+0

什麼是您的客戶端web/app.config中是什麼樣子?對於那裏的證書應該有一些值用於消息安全綁定。另外,您是否擁有正確的服務器設置上的證書存儲權限? (我認爲它通常是「我的」證書商店。這聽起來與我過去遇到的問題非常相似,並且是一個難題。我正在嘗試在我輸入時回顧它。這可能會在接下來的10年中形成,而我想到它。 – brumScouse

+0

對不起,我沒有看到服務器綁定是TransportWithMessage。但是,我確實注意到您的客戶端只使用與服務器綁定不同的傳輸綁定? – brumScouse

回答

8

您在指定客戶端使用BasicHttpSecurityMode.Transport而服務期望BasicHttpSecurityMode.TransportWithMessageCredential。這是一個問題,因爲服務正在尋找SOAP消息頭中的客戶端憑證,並且客戶端不會以這種方式發送綁定。

因此,這就是您看到的消息標題中不存在用戶名/密碼對的原因。所以事件查看器是正確的,通信雙方之間存在綁定不匹配。

同時將客戶端上的ClientCredentialType設置爲BasicHttpMessageCredentialType.UserNameMessage級別的安全性。默認BasicHttpBinding使用None這是匿名客戶端。

這裏的代碼片段描述了上述變化:

var basicHttpBinding = new BasicHttpBinding(
           BasicHttpSecurityMode.TransportWithMessageCredential); 
basicHttpBinding.Security.Message.ClientCredentialType = 
            BasicHttpMessageCredentialType.UserName; 
+0

從技術上講,你是對的,所以我給出了答案。我使用Xamarin,不幸的是底層的類不支持TransportWithMessageCredential。 – Smithy