WCF: Не удается найти мой пользовательский валидатор, указанный в web.config - PullRequest
4 голосов
/ 27 ноября 2009

Итак, я в основном все настроил и запустил с помощью wsHttpBindings и моего сервиса WCF, используя пользовательскую аутентификацию по HTTPS.

У меня проблема с customUserNamePasswordValidatorType:

  <serviceCredentials>
    <!-- Use our own custom validation -->
    <userNameAuthentication userNamePasswordValidationMode="Custom"
                            customUserNamePasswordValidatorType="CustomValidator.CustomUserNameValidator, CustomValidator"/>
  </serviceCredentials>

Найдены следующие указания здесь Я также создал свой собственный класс:

namespace CustomValidator
{
    public class CustomUserNameValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (null == userName || null == password)
            {
                throw new ArgumentNullException();
            }


            if (!AuthenticateUser(userName, password))
                throw new SecurityTokenValidationException("Invalid Credentials");

Ошибка: « Не удалось загрузить файл или сборку 'CustomValidator' или одну из его зависимостей. Система не может найти указанный файл. ", и ссылается на хвостовую часть customUserNamePasswordValidatorType - ".. ., CustomValidator ".

Я не думал, что это было проблемой с моим пользовательским валидатором в его собственном пространстве имен и классе, но я не могу понять, что еще нужно сделать, чтобы это сработало.

Я пробовал с / без пространства имен в начале, подкачки и т. Д. - ничего.

Надеясь, что другая пара глаз может выбрать это.

Спасибо.

EDIT system.serviceModel

  <system.serviceModel>
    <bindings>

      <!-- wsHttpBinding -->
      <wsHttpBinding>
        <binding name="wsHttpEndpointBinding">
          <security mode="TransportWithMessageCredential">
            <transport clientCredentialType="None" />
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </wsHttpBinding>

      <!-- webHttpBinding -->
      <webHttpBinding>
        <binding name="wsHttps" >
          <security mode="Transport"/>
        </binding>
      </webHttpBinding>

      <!-- Basic binding -->
      <basicHttpBinding>
        <binding name="TransportSecurity">
          <security mode="Transport">
            <message clientCredentialType="UserName"/>
            <!-- transport clientCredentialType="None"/-->
          </security>
        </binding>
      </basicHttpBinding>

      <!-- customBinding>
        <binding name="WebHttpBinding_IService">
          textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
              messageVersion="Soap12" writeEncoding="utf-8">
            <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          </textMessageEncoding>
          <httpsTransport manualAddressing="false"/>
        </binding>
      </customBinding -->
      <!-- Another custom binding -->
      <customBinding>
        <binding name="CustomMapper">
          <webMessageEncoding  webContentTypeMapperType=
                 "IndexingService.CustomContentTypeMapper, IndexingService" />
          <httpTransport manualAddressing="true" />
        </binding>
      </customBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
    <services>
      <service behaviorConfiguration="ServiceBehavior" name="Service">




        <!-- Service Endpoints -->
        <!-- since we're hosting in IIS, baseAddress is not required 
        <host>
          <baseAddresses>
            <add baseAddress="https://mysslserver.com/Service.svc"/>
          </baseAddresses>
        </host>
        -->
        <endpoint address="https://mysslserver.com/Service.svc"
                  binding="wsHttpBinding"
                  bindingConfiguration="wsHttpEndpointBinding" 
                  contract="IService"
                  name="wsHttpEndpoint">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <!--identity>
            <dns value="https://mysslserver.com"/>
          </identity-->
        </endpoint>

        <!-- endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/ -->
      </service>
    </services>
    <behaviors>

      <endpointBehaviors>
        <behavior name="webBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>

      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <!-- Setup Security/Error Auditing -->
          <serviceSecurityAudit auditLogLocation="Application"
                suppressAuditFailure="false"
                serviceAuthorizationAuditLevel="Failure"
                messageAuthenticationAuditLevel="Failure" />

          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"
                           httpsGetUrl="https://mysslserver.com/Service.svc"/>
          <serviceDebug includeExceptionDetailInFaults="false" />
          <serviceCredentials>
            <!-- Use our own custom validation -->
            <userNameAuthentication userNamePasswordValidationMode="Custom"
                                    customUserNamePasswordValidatorType="CustomValidator.CustomUserNameValidator, CustomValidator"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>

      <!-- serviceBehaviors>
        <behavior name="ServiceBehavior">
        <serviceMetadata httpsGetEnabled="true" 
                           httpsGetUrl="https://mysslserver.com/Service.svc" />
          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"/>
        </behavior-->
    </behaviors>
  </system.serviceModel>

Ответы [ 4 ]

14 голосов
/ 12 декабря 2009

Я решил сделать еще один удар, и мне не нравилось, когда мой пользовательский валидатор находился в другой библиотеке.

Итак, я создал новый класс в App_Code и пошел на него ...

Вот что на самом деле это исправило,

="CustomValidator.CustomUserNameValidator, App_Code"
9 голосов
/ 27 ноября 2009

Когда вы ссылаетесь на пользовательский валидатор со значениями

="CustomValidator.CustomUserNameValidator, CustomValidator"

Первое значение - это имя типа, а второе - имя сборки. в котором найти тип. Так что я бы предположил, что в вашем первом случае ваш сервис на самом деле находится в какой-то другой сборке, такой как MyService В этом случае вам действительно нужен был ваш конфигурационный файл, чтобы сказать

="CustomValidator.CustomUserNameValidator, MyService"

Я подозреваю, что когда вы создали новую библиотеку классов для валидатор, вы назвали свой проект CustomValidator (который будет выведите сборку под названием CustomValidator.dll), и, следовательно, теперь ваш Конфиг будет работать (т.е. он не имеет ничего общего с тем, чтобы быть в отдельном библиотека классов - бывает так, что в именах вашей сборки ссылка в web.config теперь действительна)

2 голосов
/ 27 ноября 2009

Кажется немного странным, но решение было создать отдельную библиотеку классов и сделать ссылку на ее DLL в моей службе WCF.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.ServiceModel;

/// <summary>
/// Summary description for CustomUsernamePasswordValidator
/// </summary>
namespace CustomValidator
{
    public class CustomUserNameValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (null == userName || null == password)
            {
                throw new ArgumentNullException();
            }


            if (!AuthenticateUser(userName, password))
                throw new SecurityTokenValidationException("Invalid Credentials");
            else
            {
                // do nothing - they're good
            }
        }

        public bool AuthenticateUser(string userName, string password)
        {
            if (userName != "userbill" || password != "passwordbill")
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
}

Затем я добавил ссылку на System.IdentityModel и System.ServiceModel.

Раздел serviceCredentials для службы WCF теперь изменен на:

<serviceCredentials>
    <!-- Use our own custom validation -->
    <userNameAuthentication userNamePasswordValidationMode="Custom"
                            customUserNamePasswordValidatorType="CustomValidator.CustomUserNameValidator, CustomValidator"/>
</serviceCredentials>

Надеюсь, это кому-нибудь поможет.

Я попытался сделать это с недействительными учетными данными и ожидал увидеть сообщение "Неверные учетные данные". Вместо этого я получаю «По крайней мере один токен безопасности в сообщении не может быть проверен».

Кроме того, эта штука наконец-то заработала!

0 голосов
/ 04 февраля 2017

Просто прочитав это, так как это было полезно для POC, я должен был начать быстро. В ответ на ELHaix выше ... это должно работать, чтобы гарантировать, что ваша описательная пользовательская ошибка возвращается клиенту:

using System.ServiceModel
...    
throw new FaultException("Invalid Credentials - Custom Error");
...