2011-05-17 157 views
1

我有一個WCF服務和客戶端,使用自定義UserNamePasswordValidator進行身份驗證。WCF身份驗證 - 用戶名密碼使用自定義驗證程序 - 適用於我的機器

我使用自簽名的證書,通過下面的命令行創建:

makecert -sr LocalMachine -ss My -a sha1 -n CN=SelfSignedCertificate -sky exchange -pe 

在服務調用方法從下面的客戶機工作正常,我的機器:)但只要我把它部署到我的服務器,我收到此錯誤消息:The request for security token could not be satisfied because authentication failed.

我可以瀏覽端點URL並查看該服務的WSDL。我不確定,但我記得我在本地機器上的IIS中配置了一些匿名身份驗證,但它在服務器上也很相似。

我的WCF服務託管在IIS 7.0中,使用此配置:

<system.serviceModel> 
    <bindings> 
     <wsHttpBinding> 
      <binding name="secured"> 
       <security mode="Message"> 
        <message clientCredentialType="UserName" /> 
       </security> 
      </binding> 
      <binding name="unsecured"> 
       <security mode="None" /> 
      </binding> 
     </wsHttpBinding> 
     <basicHttpBinding> 
      <binding name="secured"> 
       <security mode="TransportWithMessageCredential"> 
        <message clientCredentialType="UserName" /> 
       </security> 
      </binding> 
      <binding name="unsecured"> 
       <security mode="None" /> 
      </binding> 
     </basicHttpBinding> 
     <webHttpBinding> 
      <binding name="unsecured"> 
       <security mode="None" /> 
      </binding> 
     </webHttpBinding> 
    </bindings> 
    <services> 
     <service name="Milkshake.Admin.Services.AdminService" behaviorConfiguration="CustomValidator"> 
      <endpoint address="" binding="wsHttpBinding" contract="Milkshake.Admin.Model.ServiceContracts.IAdminService" bindingConfiguration="secured" /> 
     </service> 
     <service name="Milkshake.Admin.Services.DeploymentService"> 
      <endpoint address="" binding="wsHttpBinding" contract="Milkshake.Admin.Model.ServiceContracts.IDeploymentService"/> 
     </service> 
     <service name="Milkshake.Admin.Services.LogService" behaviorConfiguration="CustomValidator"> 
      <endpoint address="" binding="wsHttpBinding" contract="Milkshake.Core.Logging.ILogService" bindingConfiguration="secured" /> 
     </service> 
     <service name="Milkshake.Admin.Services.MailService"> 
      <endpoint address="" binding="wsHttpBinding" contract="Milkshake.Admin.Model.ServiceContracts.IMailService"/> 
     </service> 
    </services> 
    <behaviors> 
     <serviceBehaviors> 
      <behavior name="CustomValidator"> 
       <serviceMetadata httpGetEnabled="true" /> 
       <serviceCredentials> 
        <serviceCertificate findValue="SelfSignedCertificate" x509FindType="FindBySubjectName" /> 
        <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Milkshake.Admin.Services.MilkshakeCredentialValidator, Milkshake.Admin.Services" /> 
        <clientCertificate> 
         <authentication certificateValidationMode="None" /> 
        </clientCertificate> 
       </serviceCredentials> 
       <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" /> 
      </behavior> 
     </serviceBehaviors> 

     <endpointBehaviors> 
      <behavior name="json"> 
       <enableWebScript /> 
      </behavior> 

      <behavior name="xml"> 
       <webHttp defaultOutgoingResponseFormat="Xml" defaultBodyStyle="Wrapped" /> 
      </behavior> 
     </endpointBehaviors> 
    </behaviors> 
</system.serviceModel> 

客戶端配置是這樣的:

<system.serviceModel> 
    <client> 
     <endpoint address="http://server-url/LogService.svc" binding="wsHttpBinding" contract="Milkshake.Core.Logging.ILogService"> 
      <identity> 
       <dns value="SelfSignedCertificate" /> 
      </identity> 
     </endpoint> 
    </client> 
</system.serviceModel> 

我自定義的驗證是這樣的:

using System; 
using System.IdentityModel.Selectors; 
using System.ServiceModel; 

namespace Milkshake.Admin.Services 
{ 
    /// <summary> 
    /// WCF Service validator for Milkshake. 
    /// </summary> 
    public class MilkshakeCredentialValidator : UserNamePasswordValidator 
    { 
     /// <summary> 
     /// When overridden in a derived class, validates the specified username and password. 
     /// </summary> 
     /// <param name="userName">The username to validate.</param> 
     /// <param name="password">The password to validate.</param> 
     public override void Validate(string userName, string password) 
     { 
      if (String.IsNullOrWhiteSpace(userName) || String.IsNullOrWhiteSpace(password)) 
      { 
       throw new ArgumentNullException(); 
      } 

      if (userName.Equals("martin") && password.Equals("normark")) 
      { 
       return; 
      } 

      FaultCode fc = new FaultCode("ValidationFailed"); 
      FaultReason fr = new FaultReason("Good reason"); 

      throw new FaultException(fr, fc); 
     } 
    } 
} 

我的服務客戶端,如下所示:

using System; 
using System.ServiceModel; 
using System.ServiceModel.Security; 
using Milkshake.Core.Logging; 

namespace Milkshake.Admin.ServiceClients.Logging 
{ 
    /// <summary> 
    /// WCF Service Client implementation of the <see cref="ILogService"/> contract. 
    /// </summary> 
    public class LogServiceClient : ILogService 
    { 
     /// <summary> 
     /// Initializes a new instance of the <see cref="LogServiceClient"/> class. 
     /// </summary> 
     public LogServiceClient() 
     { 
      var factory = new ChannelFactory<ILogService>(String.Empty); 
      factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None; 
      factory.Credentials.UserName.UserName = "martin"; 
      factory.Credentials.UserName.Password = "normark"; 

      var binding = factory.Endpoint.Binding as WSHttpBinding; 

      if (binding != null) 
      { 
       binding.Security.Mode = SecurityMode.Message; 
       binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName; 
      } 

      this.AdminLogService = factory.CreateChannel(); 
     } 

     /// <summary> 
     /// Gets or sets the admin log service. 
     /// </summary> 
     /// <value>The admin log service.</value> 
     private ILogService AdminLogService { get; set; } 

     #region ILogService Members 

     /// <summary> 
     /// Logs the error simple. 
     /// </summary> 
     /// <param name="applicationInstanceId">The application instance id.</param> 
     /// <param name="logDate">The log date.</param> 
     /// <param name="exceptionMessage">The exception message.</param> 
     /// <param name="description">The description.</param> 
     /// <param name="severity">The severity.</param> 
     /// <param name="userAgent">The user agent.</param> 
     /// <param name="source">The source.</param> 
     public void LogErrorSimple(int applicationInstanceId, DateTime logDate, string exceptionMessage, string description, Severity severity, string userAgent, string source) 
     { 
      this.AdminLogService.LogErrorSimple(applicationInstanceId, logDate, exceptionMessage, description, severity, userAgent, source); 
     } 

     /// <summary> 
     /// Logs the error advanced. 
     /// </summary> 
     /// <param name="applicationInstanceId">The application instance id.</param> 
     /// <param name="logDate">The log date.</param> 
     /// <param name="exceptionType">Type of the exception.</param> 
     /// <param name="exceptionCode">The exception code.</param> 
     /// <param name="exceptionMessage">The exception message.</param> 
     /// <param name="stackTrace">The stack trace.</param> 
     /// <param name="caller">The caller.</param> 
     /// <param name="location">The location.</param> 
     /// <param name="source">The source (A service, app etc).</param> 
     /// <param name="description">The description.</param> 
     /// <param name="severity">The severity.</param> 
     /// <param name="username">The username.</param> 
     /// <param name="userAgent">The user agent.</param> 
     public void LogErrorAdvanced(int applicationInstanceId, DateTime logDate, string exceptionType, string exceptionCode, string exceptionMessage, string stackTrace, string caller, string location, string source, string description, Severity severity, string username, string userAgent) 
     { 
      this.AdminLogService.LogErrorAdvanced(applicationInstanceId, logDate, exceptionType, exceptionCode, exceptionMessage, stackTrace, caller, location, source, description, severity, userAgent, userAgent); 
     } 

     /// <summary> 
     /// Logs the behavior with data. 
     /// </summary> 
     /// <param name="applicationInstanceId">The application instance id.</param> 
     /// <param name="action">The action.</param> 
     /// <param name="logDate">The log date.</param> 
     /// <param name="userAgent">The user agent.</param> 
     /// <param name="behaviorData">The behavior data.</param> 
     /// <param name="source">The source.</param> 
     public void LogBehaviorWithData(int applicationInstanceId, string action, DateTime logDate, string userAgent, string behaviorData, string source) 
     { 
      this.AdminLogService.LogBehaviorWithData(applicationInstanceId, action, logDate, userAgent, behaviorData, source); 
     } 

     /// <summary> 
     /// Logs the behavior. 
     /// </summary> 
     /// <param name="applicationInstanceId">The application instance id.</param> 
     /// <param name="action">The action.</param> 
     /// <param name="logDate">The log date.</param> 
     /// <param name="userAgent">The user agent.</param> 
     /// <param name="source">The source.</param> 
     public void LogBehavior(int applicationInstanceId, string action, DateTime logDate, string userAgent, string source) 
     { 
      this.AdminLogService.LogBehavior(applicationInstanceId, action, logDate, userAgent, source); 
     } 

     #endregion 
    } 
} 
+0

是服務器上已知的自簽名證書嗎? – Menahem 2011-05-17 12:45:50

+0

是的,它位於本地機器上的個人存儲中。如果我將配置文件更改爲不正確的證書名稱,則會出現一個錯誤,專門指出無法找到證書。 – MartinHN 2011-05-17 12:47:46

+0

我不確定'basicHttpBinding'是否支持Message安全中的用戶名憑證。是否有可能嘗試使用wsHttpBinding? – Menahem 2011-05-17 13:14:45

回答

1

我還使用UserNamePasswordValidator和使用makecert生成的證書。

不使用IIS,而是使用WcfSvcHost,以確保它可以成功啓動。我提出這個建議的原因是,證書鏈建設可能失敗。有一個設置可以用來禁用鏈式構建。 (更新:我認爲你已經在使用CertificateValidationMode =「none」)

我注意到的另一件事是,並非所有的服務定義都指定behaviourConfiguration。這是一個小山毛櫸找到,因爲它有時由VS更新您的服務引用時被刪除。

你提到的異常是從最內部異常的消息?

+0

這是最內部的例外。我只是加倍檢查。 關於IIS,有沒有辦法禁用鏈建設?我會馬上嘗試WcfSvcHost。 – MartinHN 2011-05-17 13:48:49

+0

有沒有簡單的方法在服務器上使用WcfServiceHost? VS2010沒有安裝在那裏... – MartinHN 2011-05-17 13:53:52

+0

想了一下問題是你的客戶端配置,但我現在看到你通過代碼來做到這一點。是否有一個原因,你爲什麼不使用配置文件? – 2011-05-17 13:54:54

相關問題