2011-05-17 103 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

相关问题