带有用户名身份验证的 WCF 中的异常

Posted

技术标签:

【中文标题】带有用户名身份验证的 WCF 中的异常【英文标题】:Exception in WCF with UserName Authentication 【发布时间】:2020-04-24 06:01:34 【问题描述】:

我正在尝试使用自定义用户名和密码身份验证对 WCF 进行身份验证。我关注了this tutorial by Microsoft。在本地开发中,一切正常,但是一旦部署在服务器中,我就无法连接到该服务。 每当我尝试访问服务中的方法时,它都会在客户端中引发此异常:

无法打开安全通道,因为在与远端的安全协商中产生了错误。这可能是因为在用于创建通道的 EndpointAddress 元素中缺少 EndpointIdentity 或未正确指定。验证 EndpointAddress 中指定或隐含的 EndpointIdentity 元素是否正确标识了远程端点。

看来我已经完成了所需的一切。首先,我在客户端创建了一个证书 使用这个 PowerShell 命令:

New-SelfSignedCertificate -DnsName desasqlapps01.ad.impi.gob.mx -CertStoreLocation cert:\LocalMachine\My -KeySpec KeyExchange


 $CertPassword = ConvertTo-SecureString -String “PruebaCertificadoAutoFirma” -Force –AsPlainText
 Export-PfxCertificate -Cert cert:\LocalMachine\My\E7A3FCD624E6FBD74B1EB346F0A8E0CA38FA24A3 - 
 FilePath C:\testcerttramitemixto.pfx -Password $CertPassword

我在服务中创建了一个派生自UserNamePasswordValidator的类,服务配置的相关部分是:

    <system.web>
    <compilation debug="true" targetFramework="4.7.2" />
    <httpRuntime targetFramework="4.7.2" maxRequestLength="2147483647"/>
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WS_TramiteMixto.IServiceTramiteMixto">

        <host>
          <baseAddresses>
            <add baseAddress ="http://desasqlapps01.ad.impi.gob.mx/" />
          </baseAddresses>
        </host>
        <endpoint address=""
                    binding="wsHttpBinding"
                    contract="WS_TramiteMixto.IServiceTramiteMixto"></endpoint>
        <!--<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />-->

      </service>

    </services>
    <bindings>

      <!--<basicHttpBinding>

        <binding maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" receiveTimeout="00:30:00" sendTimeout="00:30:00">

          <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="16384"/>
        </binding>
      </basicHttpBinding>-->

      <wsHttpBinding>
        <binding maxReceivedMessageSize="2147483647" receiveTimeout="00:30:00" sendTimeout="00:30:00">
          <security mode="Message">
            <message clientCredentialType="UserName" establishSecurityContext="true" />
          </security>
          <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="16384"/>
        </binding>
      </wsHttpBinding>
      <webHttpBinding>
        <binding maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"/>
      </webHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceCredentials>


            <!--The serviceCredentials behavior allows one to
          specify a custom validator for username/password
          combinations.
          -->

            <userNameAuthentication userNamePasswordValidationMode="Custom"
                                    customUserNamePasswordValidatorType="WS_TramiteMixto.TramiteMixtoPaseValidator, WS_TramiteMixto" />


            <!--The serviceCredentials behavior allows one to define a service certificate. A service certificate is used by a client to authenticate the service and provide message protection. You must specify a server certificate when passing username/passwords to encrypt the information as it is sent on the wire. Otherwise the username and password information would be sent as clear text. This configuration references the "localhost" certificate installed during the setup instructions.
          -->

            <serviceCertificate findValue="desasqlapps01.ad.impi.gob.mx" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
          </serviceCredentials>
          <dataContractSerializer maxItemsInObjectGraph ="2147483647"/>
          <!-- Para evitar revelar información de los metadatos, establezca el valor siguiente en false antes de la implementación -->
          <serviceMetadata httpGetEnabled="True" policyVersion="Policy12" />
          <!-- Para recibir detalles de los errores de la excepción para la depuración, establezca el valor siguiente en true. Establézcalo en false antes de la implementación para evitar revelar información de la excepción -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="wsHttpBinding" scheme="http"/>
      <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

然后客户端是这样配置的:

<behaviors>
      <endpointBehaviors>
        <behavior name="webHttp">
          <webHttp />
        </behavior>
        <behavior name="ClientCertificateBehavior">
          <clientCredentials>
            <serviceCertificate>

           <!-- setting the certificatevalidationmode to peerorchaintrust means that if the certificate
            is in the user's trusted people store, then it will be trusted without performing a
            validation of the certificate's issuer chain. this setting is used here for convenience so that the
            sample can be run without having to have certificates issued by a certification authority (ca).
            this setting is less secure than the default, chaintrust. the security implications of this
            setting should be carefully considered before using peerorchaintrust in production code. -->

              <authentication certificateValidationMode="PeerOrChainTrust" />
            </serviceCertificate>
          </clientCredentials>
        </behavior>

      </endpointBehaviors>
<bindings>
    <wsHttpBinding>
            <binding name="WSHttpBinding_IService1" />
            <binding name="WSHttpBinding_IServiceTramiteMixto">
              <security mode="Message">
                <message clientCredentialType="UserName" establishSecurityContext="true" negotiateServiceCredential="true" />
              </security>
            </binding>
          </wsHttpBinding>
</bindings>
<endpoint address="http://desasqlapps01.ad.impi.gob.mx/WS_TramiteMIxto/ServiceTramiteMixto.svc"
  binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IServiceTramiteMixto"
  contract="ServicioTramiteMixto.IServiceTramiteMixto" name="WSHttpBinding_IServiceTramiteMixto">
      <!--<endpoint address="http://desasqlapps01.ad.impi.gob.mx/WS_TramiteMIxto/ServiceTramiteMixto.svc"
        binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IServiceTramiteMixto"
        contract="ServicioTramiteMixto.IServiceTramiteMixto" name="WSHttpBinding_IServiceTramiteMixto" behaviorConfiguration="ClientCertificateBehavior">-->
        <!--<identity>
          <dns value="http://desasqlapps01.ad.impi.gob.mx/"></dns>
        </identity>-->
        <identity>
          <certificate encodedValue="AwAAAAEAAAAUAAAAN9gRCkWxQeyqhRXP90pEMclVHZcgAAAAAQAAAFUDAAAwggNRMIICOaADAgECAhAo9Mgx1zrktUgP8eprE972MA0GCSqGSIb3DQEBBQUAMCcxJTAjBgNVBAMMHGRlc2FzcWxhcHBzMDEuYWQuaW1waS5nb2IubXgwHhcNMjAwMTAzMTkzOTAzWhcNMjEwMTAzMTk1OTAzWjAnMSUwIwYDVQQDDBxkZXNhc3FsYXBwczAxLmFkLmltcGkuZ29iLm14MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYU9MV1Au0gmf043luz5ADknADdvOI+ufk52GP+q9pW7MpcCvyaZXd0OG7EeaeUMU9WriOX/TQrWrFQjqM06EXXzixEd7cF7qU2i0W3rpccrBQrhKollkoiOkkiQuXgHjqFFUMVlpeiCwRELjHGLeVnLTEnz9RzxKTUghtqHHkVECvkwuY6jVbUAUOY0+kEHPoSyPfYnIztSfWLd6mCDdMzRJ+vhSsCbzHFBQnWGwpQj5d58+XoLz9FPssmXjl/iPzZGmw+bPt7XT1y+OYNu7W0QsXYdcNJyzjjlwieiciEm7dPq4V3lQaIxkIlkRHRG6bHldYy9EI+esqZVJs4cvQIDAQABo3kwdzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMCcGA1UdEQQgMB6CHGRlc2FzcWxhcHBzMDEuYWQuaW1waS5nb2IubXgwHQYDVR0OBBYEFC44LUubIpku7E8LvAxzOvSQkx83MA0GCSqGSIb3DQEBBQUAA4IBAQAxs4NLEVcpg8JIqThchTGYXripNyC5twZRn/SNsgVnNV5zSMzlPpwrDdpHwPbB5QNrm8XBfAIayWP0IjjB2LyMY33gIs1dNAM6xXffWNg3m6wOwMG/QomcZvWEfdUKGLG/AVwLQ61gR9NZO6I2xeRyLc7uRM8QYsu0vASjrUKjDNaBlGXxKLEJCcqC9ybQjaVas1ICY7n4rEP9PKr6Gmck2Pva6gVYJpguihhBeN1OYIsYPABP/qRoXmU9nt7jNKnJh9IxxaPt8yhR2kMlQOunbXoV6wVKSq484/9vikk3tTni7Yb44EiFoyyxscOq/Spzq+HsVm6hitQwk/cM2ID6" />
        </identity>
      </endpoint>

我已将证书放置在客户端的 Trusted People 存储区和服务器中当前用户的 Personal 存储区中,但我得到了该异常,我无法调用服务中的任何方法。我还在服务中启用了跟踪,但没有记录任何内容,因此客户端似乎甚至无法访问该服务。

根据this post,我已读到此错误可能是由 IIS 中的负载平衡引起的,我将服务作为应用程序托管在其中,但 Microsoft 声明要处理 IIS 中的负载平衡,您应该使用安全会话,从here 我已经在服务绑定中的安全类型中做这件事:&lt;security mode="Message"&gt;

其中一个要求是对该服务的所有传入请求进行身份验证。

更新: 顺便说一句,&lt;endpoint address="http://desasqlapps01.ad.impi.gob.mx/WS_TramiteMIxto/ServiceTramiteMixto.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IServiceTramiteMixto" contract="ServicioTramiteMixto.IServiceTramiteMixto" name="WSHttpBinding_IServiceTramiteMixto" behaviorConfiguration="ClientCertificateBehavior"&gt; 的注释端点是实际配置,cmets 是后来添加的。

@Ross Bush 谢谢你的回答 我在服务中编辑了 Web 配置,如下所示:

         <wsHttpBinding>
        <binding maxReceivedMessageSize="2147483647" receiveTimeout="00:30:00" sendTimeout="00:30:00">
          <security mode="Transport">
<transport clientCredentialType="Certificate"/>
        <message clientCredentialType="UserName" />
        </security>
          <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="16384"/>
        </binding>
      </wsHttpBinding>
      <webHttpBinding>
        <binding maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"/>
      </webHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <dataContractSerializer maxItemsInObjectGraph ="2147483647"/>
          <!-- Para evitar revelar información de los metadatos, establezca el valor siguiente en false antes de la implementación -->
          <serviceMetadata httpGetEnabled="True" policyVersion="Policy12" />
          <!-- Para recibir detalles de los errores de la excepción para la depuración, establezca el valor siguiente en true. Establézcalo en false antes de la implementación para evitar revelar información de la excepción -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="wsHttpBinding" scheme="http"/>

    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

但我不知道您建议的配置是否仅在 https 中有效:我知道收到运行时错误提示

找不到与具有绑定 WSHttpBinding 的端点的方案 https 匹配的基地址。注册的基地址方案是http

并且该服务现在没有运行。

@Ross Bush 没关系,我已经解决了它添加

<add binding="wsHttpBinding" scheme="https"/>

</protocolMapping>

到服务的配置

根据我收到的 cmets,我想重新表述我的问题,特别是我想知道 - 如果有人可以告诉我 -

我的证书有效吗? 是否可以在不使用 ssl 的情况下在 wcf 上设置用户名密码验证?

微软的教程只说明:

您需要具有包含机器完全限定域名的主题名称的服务器证书。必须更新服务器的配置文件以反映此新证书名称

但它没有说明使用 SSL 或 SSL 证书。 预先感谢。

【问题讨论】:

是的,我提交的答案只能在 https 下工作。我删除了它,因为它不会解决您的特定问题。如果您继续采用这种方法,那么您需要有一个带有 wsHttpBinding 绑定的端点,抱歉,不清楚。 【参考方案1】:

不需要使用 HTTPS,就像你配置的一样。真的行。但是,无论使用 https 还是配置服务证书,我们至少在服务器端指定一个证书。

<system.serviceModel>
    <services>
      <service name="WcfService3.Service1" behaviorConfiguration="mybehavior">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="mybinding" contract="WcfService3.IService1"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <binding name="mybinding">
          <security mode="Message">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="mybehavior">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceCredentials>
            <serviceCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" findValue="5ba5022f527e32ac02548fc5afc558de1d314cb6"/>
            <userNameAuthentication customUserNamePasswordValidatorType="WcfService3.CustUserNamePasswordVal,WcfService3" userNamePasswordValidationMode="Custom"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

不知道你的证书域名是否对应服务器机器名?我建议您通过添加服务引用来生成客户端代理类。https://docs.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client 客户端生成的端点配置会自动添加一个 EndpointIdentity。

  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_IService1">
          <security>
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://vabqia969vm:8818/Service1.svc" binding="wsHttpBinding"
        bindingConfiguration="WSHttpBinding_IService1" contract="ServiceReference1.IService1"
        name="WSHttpBinding_IService1">
        <identity>
          <certificate encodedValue="blablabla" />
<!--or,using dns to identity the server -->
          <!--<dns value="vabqia969vm"/>-->
        </identity>
      </endpoint>
    </client>

另外,请移除webhttpbinding相关配置。这与问题无关。 如果问题仍然存在,请随时告诉我。

【讨论】:

@AbrahamQlan 我已经有了那个配置,正如你自己所说的,当我在 VS 中添加服务引用时,它也会生成一个身份。但是有一个问题,¿证书可以像我所做的那样是自签名的,还是必须是 CA? 两者都可以。问题是应该在调用服务之前建立信任关系。为了做到这一点,在客户端,我们通常将服务器证书安装在证书存储的受信任的根证书颁发机构中。或编写证书验证回调。 ServicePointManager.ServerCertificateValidationCallback += delegate return true; ; ServiceReference1.ServiceClient 客户端 = new ServiceReference1.ServiceClient();

以上是关于带有用户名身份验证的 WCF 中的异常的主要内容,如果未能解决你的问题,请参考以下文章

WCF 用户身份验证和授权

在 SoapUI 上进行用户名身份验证测试的 WCF WebService

创建 WCF 休息服务以接受 SAML 并对 Windows 用户进行身份验证

在声明身份验证的 SharePoint 2010 中托管的 WCF 服务中的模拟

带有客户端证书身份验证的 Wcf 不适用于 soapui

在带有 WCF 的 App.config 中使用 Windows 角色身份验证