WCF:找不到 web.config 中指定的自定义验证器 - customUserNamePasswordValidatorType - - 无法加载文件或程序集... - 帮助?

发布于 2024-08-12 18:48:42 字数 6447 浏览 2 评论 0原文

因此,我基本上已经使用 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”。

我不认为将自定义验证器放在自己的命名空间和类中是一个问题,但我不知道还能做什么来使这项工作正常进行。

我尝试过在开始时使用/不使用名称空间、交换等 - 什么都没有。

希望另一双眼睛能看清这一点。

谢谢。

编辑 系统.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>

So I've basically got everything up and running with wsHttpBindings and my WCF service using custom authentication over HTTPS.

The issue I'm having is with the customUserNamePasswordValidatorType:

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

Following directions found here I've created my custom class as well:

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");

The error is "Could not load file or assembly 'CustomValidator' or one of its dependencies. The system cannot find the file specified.", and refers to the tail end of customUserNamePasswordValidatorType - "..., CustomValidator".

I didn't think it was a problem having my custom validator in its own namespace and class, but I can't see what else to do to make this work.

I've tried with/without the namespace at the beginning, swapping, etc - nothing.

Hoping another pair of eyes can pick this out.

Thanks.

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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

顾忌 2024-08-19 18:48:42

我决定再试一次,并且不喜欢将我的自定义验证器放在另一个库中。

所以我在 App_Code 中创建了一个新类,然后继续...

以下是实际修复它的内容,

="CustomValidator.CustomUserNameValidator, App_Code"

I decided to give it another stab, and didn't like having my custom validator in another lib.

So I created a new class in App_Code, and went at it...

The following is what actually fixed it,

="CustomValidator.CustomUserNameValidator, App_Code"
苯莒 2024-08-19 18:48:42

当您使用值引用自定义验证器时

="CustomValidator.CustomUserNameValidator, CustomValidator"

第一个值是类型名称,第二个值是程序集名称
在其中查找类型。所以我建议你首先
您的服务实际上位于其他一些程序集中,例如 MyService
在这种情况下,您确实需要您的配置文件来说明

="CustomValidator.CustomUserNameValidator, MyService"

我怀疑当您为您的项目创建新的类库时
验证器,您已将您的项目称为 CustomValidator (它将
输出一个名为 CustomValidator.dll 的程序集),因此现在您的
配置将起作用(即它与处于单独的
类库 - 碰巧你的程序集的命名
web.config 中的引用现在有效)

When you refer to the custom validator with the values

="CustomValidator.CustomUserNameValidator, CustomValidator"

The first value is the type name and the second is the name of the assembly
in which to find the type. So I would suggest that in your first instance
your service is actually in some other assembly such as MyService
In that case you really needed your config file to say

="CustomValidator.CustomUserNameValidator, MyService"

I suspect that when you have created your new class library for your
validator, you have called your project CustomValidator (which will
output an assembly called CustomValidator.dll), and hence now your
config will work (i.e. it has nothing to do with being in a separate
class library - it just happens that the naming of your assembly
reference in the web.config is now valid)

旧人九事 2024-08-19 18:48:42

看起来有点奇怪,但解决方案是创建一个单独的类库并在我的 WCF 服务中引用它的 DLL。

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 的引用。

WCF 服务的 serviceCredentials 部分现已更改为:

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

希望对某人有所帮助。

我使用无效的凭据尝试了此操作,并希望看到“无效的凭据”消息。相反,我收到“消息中至少有一个安全令牌无法验证”。

除此之外,这件事终于启动并运行了!

Seems a bit strange, but the solution was to create a separate class library and make reference to its DLL in my WCF service.

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;
            }
        }
    }
}

I then made added a reference to System.IdentityModel and System.ServiceModel.

The serviceCredentials section for the WCF service is now changed to this:

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

Hope that helps someone.

I tried this with invalid credentials, and was expecting to see my "Invalid Credentials" message. Instead I'm getting "At least one security token in the message could not be validated."

Other than that this thing is finally up and running!

向地狱狂奔 2024-08-19 18:48:42

只是阅读这篇文章,因为它对我必须快速开始的 POC 有帮助。响应上面的 ELHaix...这应该可以确保您的描述性自定义错误返回给客户端:

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

Just reading this as it was helpful for a POC I had to get going quickly. In response to ELHaix above...this should work to ensure your descriptive custom error is returned back to the client:

using System.ServiceModel
...    
throw new FaultException("Invalid Credentials - Custom Error");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文