WCF客户端使用证书和用户名/密码凭据?

时间:2022-08-13 15:26:04

I am consuming a web service internal to my company from ASP.NET. I've used svcutil.exe to connect to the service and generate the bindings and classes from the wsdl. I am able to connect to the dev version, which does not require authentication. Now we are adding in security. My new URI uses https but also requires user credentials.

我从ASP.NET使用我公司内部的Web服务。我已经使用svcutil.exe连接到服务并从wsdl生成绑定和类。我能够连接到开发版本,不需要身份验证。现在我们正在增加安全性。我的新URI使用https,但也需要用户凭据。

I am very new to WCF, and am trying to determine the way to configure this. From my reading on MSDN, it appears that the way to go is using.

我是WCF的新手,我正在尝试确定配置它的方法。根据我在MSDN上的阅读,似乎要走的路是使用。

UPDATE: Here is the most recent code I've been trying. This incorporates feedback from the answer(s):

更新:这是我一直在尝试的最新代码。这包含了答案的反馈:

 <system.serviceModel>
   <behaviors>
     <serviceBehaviors>
       <behavior name="svcBehavior">
         <serviceCredentials>
           <serviceCertificate storeLocation="CurrentUser"
                               storeName="My"
                               x509FindType="FindByThumbprint"
                               findValue="xx xx xx etc"/>
         </serviceCredentials>
       </behavior>
     </serviceBehaviors>
   </behaviors>
   <bindings>
     <wsHttpBinding>
       <binding name="CustomerPaymentProgramSOAPBinding">
         <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName" />
         </security>
       </binding>
     </wsHttpBinding>
   </bindings>
  <client>
   <endpoint address="https://***URL***"
    binding="wsHttpBinding" bindingConfiguration="CustomerPaymentProgramSOAPBinding"
    contract="CppService.CustomerPaymentProgramService" name="CustomerPaymentProgramService">
   </endpoint>
  </client>
 </system.serviceModel>

Here is the calling code:

这是调用代码:

using (var svc = new CustomerPaymentProgramServiceClient())
{
    svc.ClientCredentials.UserName.UserName = "*******";
    svc.ClientCredentials.UserName.Password = "*******";
    var request = new GetServiceDataProgramRequest()
                      {
                          CustomerAccountId = Convert.ToInt64(customerAccountId)
                      };

    svc.Open();
    var response = new GetServiceDataProgramResponse();
    var metaData = new RequestMetadata()
                       {
                           ClientIPAddress = "xx.xx.xx.xx",
                           TrackingNumber = "1",
                           UserID = "1"
                       };

    svc.GetAccountData(metaData, request, out response);
}

I am getting an error stating I am passing anonymous credentials with the request. With the updated code, now I receive a different exception:

我收到一条错误,指出我正在通过请求传递匿名凭据。使用更新的代码,现在我收到一个不同的例外:

UPDATE:

After making suggested changes as well as removing the service call from the using block (per this article), I'm now getting a MessageSecurityException:

在进行建议的更改以及从using块中删除服务调用之后(根据本文),我现在收到MessageSecurityException:

Error message:
-$exception {"The HTTP request is unauthorized with client authentication scheme 'Anonymous'. 
The authentication header received from the server was 'Basic realm=\"Spring Security Application\"'."} 
System.Exception {System.ServiceModel.Security.MessageSecurityException}


Server stack trace: 
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateAuthentication(HttpWebRequest request, HttpWebResponse response, WebException responseException, HttpChannelFactory factory)
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory factory, WebException responseException)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation, EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
at System.ServiceModel.Channels.ServiceChannel.EnsureOpened(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]: 
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at [ServiceName].GetAccountData(svcRequest request)
at [ServiceName].GetAccountData(GetAccountDataRequest request) 
    in c:\\[Project]\\service references\\[ServiceName]\\reference.cs:line 3480
at c:\\[Project]\\service references\\[ServiceName](RequestMetadata RequestMetadata, ServiceRequest, ServiceResponse& ServiceResponse) 
    in c:\\[Project]\\service references\\[ServiceName]\\reference.cs:line 3487
at c:\\[Project]\\service references\\[ServiceName].CheckAccountForPaymentPlan(String customerAccountId) 
    in c:\\[Project]\\service references\\[ServiceName]\\\\PlanCheckService.cs:line 32

1 个解决方案

#1


With TransportWithMessageCredential, you're specifying that you'll use message security to secure the individual messages, which requires the client credentials (username and password). You need something similar to the config from the msdn link as below:

使用TransportWithMessageCredential,您将指定使用邮件安全性来保护单个邮件,这需要客户端凭据(用户名和密码)。您需要与msdn链接中的配置类似的内容,如下所示:

<wsHttpBinding>
<binding name="WsHttpBinding_ICalculator">
        <security mode="TransportWithMessageCredential" >
           <message clientCredentialType="UserName" />
        </security>
</binding>
</wsHttpBinding>

This doesn't require specifying a certificate for client credentials. Transport security (using https and a valid ssl certificate) works in the same way a web site does, it doesn't require additional credentials/certs from the user. Valid certs from trusted certificate authorities are installed on the server (client machines are able to validate them) and the handshake process secures the channel. This does not require you to set clientCrdentials in config. You just need to install a valid cert (or test cert for dev) and configure the server config to point to it with something similar to:

这不需要为客户端凭据指定证书。传输安全性(使用https和有效的ssl证书)的工作方式与网站相同,不需要用户提供额外的凭据/证书。来自可信证书颁发机构的有效证书安装在服务器上(客户端计算机能够验证它们),并且握手过程可以保护通道。这不需要您在config中设置clientCrdentials。您只需要安装有效的证书(或dev的测试证书)并配置服务器配置以指向它,类似于:

<behaviors>
 <serviceBehaviors>
   <behavior name="mySvcBehavior">
       <serviceCredentials>
         <serviceCertificate findValue="contoso.com"
                             x509FindType="FindByIssuerName" />
       </serviceCredentials>
   </behavior>
 </serviceBehaviors>
</behaviors>

Try to remove the < transport clientCredentialType="Certificate" /> from your server config as a starter, update service refs and ensure your cert is working and configured correctly. Post your actual exceptions and more config if you still have problems.

尝试从服务器配置中删除 作为启动器,更新服务引用并确保您的证书正常工作和配置。如果仍有问题,请发布您的实际异常和更多配置。

For a good WCF source try: CodePlex, it helped me out no end when I started with WCF. The different application scenarios provide useful checklists to help ensure you don't miss any steps in your configuration.

对于一个好的WCF源代码尝试:CodePlex,当我开始使用WCF时,它帮助我永无止境。不同的应用程序方案提供了有用的清单,以帮助确保您不会错过配置中的任何步骤。

Good Luck

UPDATE:

Once a channel is faulted, it needs to be recreated as you won't be able to communicate with the service until it's been reset. So add a check to recreate it:

一旦通道出现故障,就需要重新创建,因为在重置之前,您将无法与服务进行通信。所以添加一个检查来重新创建它:

If svc.State = CommunicationState.Faulted Then....

Try remove the svc.Open() line as I've never actually used that. I checked msdn for usage details but got about 2 lines of useless info. Once the service is setup you should be able to communicate with it without having to open it specifically. Not sure that this will actually make a difference though?!

尝试删除svc.Open()行,因为我从未实际使用过它。我检查了msdn的使用细节,但得到了大约2行无用的信息。设置服务后,您应该能够与其进行通信,而无需专门打开它。不确定这实际上会有所作为吗?!

Other things to check: - can you right click on the service and view in browser without problems? - in IIS can you view the certificate in directory security without any issues? - debug up to the point before the service call is made and check the credentials are correctly assigned. - check server event viewer for any info it may have logged with the request (if it's getting that far).

其他要检查的事项: - 您是否可以右键单击服务并在浏览器中查看而不会出现问题? - 在IIS中,您可以在目录安全性中查看证书而不会出现任何问题吗? - 调试到服务调用之前的点,并检查凭据是否正确分配。 - 检查服务器事件查看器,查看它可能记录在请求中的任何信息(如果它已经到了那么远)。

Also, here's some of the exception I trap to determine issues using ex.GetBaseException.GetType:

另外,这里有一些异常,我使用ex.GetBaseException.GetType来确定问题:

ServiceModel.EndpointNotFoundException

  • Server connection problem - either IIS isn't running or invalid server name
  • 服务器连接问题 - IIS未运行或服务器名称无效

ServiceModel.Security.MessageSecurityException

  • Base Exception - ServiceModel.FaultException

    基本异常 - ServiceModel.FaultException

    • "FailedAuthentication" - Bad credentials entered by user

      “FailedAuthentication” - 用户输入的凭据错误

    • "InvalidSecurity" - DB Error - either account has no access to DB, DB name in web config is incorrect, user password has expired in the database

      “InvalidSecurity” - 数据库错误 - 帐户无权访问数据库,Web配置中的数据库名称不正确,用户密码已在数据库中过期

    • "InvalidRequest" - Certificate accessibility issue - check service account has access to certificate

      “InvalidRequest” - 证书可访问性问题 - 检查服务帐户是否可以访问证书

Base Exception - Net.WebException

基本异常 - Net.WebException

  • Unauthorized access 401 - Check anonymous access in IIS is turned on
  • 未经授权的访问401 - 检查IIS中的匿名访问是否已打开

Base Exception - IdentityModel.Tokens.SecurityTokenValidationException

基本异常 - IdentityModel.Tokens.SecurityTokenValidationException

  • No service certificate is assigned in IIS
  • IIS中未分配服务证书

Base Exception - System.ServiceModel.CommunicationException

基本异常 - System.ServiceModel.CommunicationException

  • Identity mismatch with server certificate - eg in dev environment, cert named "localhost" so if you enter PC number or IP address for service you'll see this
  • 身份与服务器证书不匹配 - 例如在开发环境中,证书名为“localhost”,因此如果您输入PC号码或IP地址进行维修,您将看到此信息

#1


With TransportWithMessageCredential, you're specifying that you'll use message security to secure the individual messages, which requires the client credentials (username and password). You need something similar to the config from the msdn link as below:

使用TransportWithMessageCredential,您将指定使用邮件安全性来保护单个邮件,这需要客户端凭据(用户名和密码)。您需要与msdn链接中的配置类似的内容,如下所示:

<wsHttpBinding>
<binding name="WsHttpBinding_ICalculator">
        <security mode="TransportWithMessageCredential" >
           <message clientCredentialType="UserName" />
        </security>
</binding>
</wsHttpBinding>

This doesn't require specifying a certificate for client credentials. Transport security (using https and a valid ssl certificate) works in the same way a web site does, it doesn't require additional credentials/certs from the user. Valid certs from trusted certificate authorities are installed on the server (client machines are able to validate them) and the handshake process secures the channel. This does not require you to set clientCrdentials in config. You just need to install a valid cert (or test cert for dev) and configure the server config to point to it with something similar to:

这不需要为客户端凭据指定证书。传输安全性(使用https和有效的ssl证书)的工作方式与网站相同,不需要用户提供额外的凭据/证书。来自可信证书颁发机构的有效证书安装在服务器上(客户端计算机能够验证它们),并且握手过程可以保护通道。这不需要您在config中设置clientCrdentials。您只需要安装有效的证书(或dev的测试证书)并配置服务器配置以指向它,类似于:

<behaviors>
 <serviceBehaviors>
   <behavior name="mySvcBehavior">
       <serviceCredentials>
         <serviceCertificate findValue="contoso.com"
                             x509FindType="FindByIssuerName" />
       </serviceCredentials>
   </behavior>
 </serviceBehaviors>
</behaviors>

Try to remove the < transport clientCredentialType="Certificate" /> from your server config as a starter, update service refs and ensure your cert is working and configured correctly. Post your actual exceptions and more config if you still have problems.

尝试从服务器配置中删除 作为启动器,更新服务引用并确保您的证书正常工作和配置。如果仍有问题,请发布您的实际异常和更多配置。

For a good WCF source try: CodePlex, it helped me out no end when I started with WCF. The different application scenarios provide useful checklists to help ensure you don't miss any steps in your configuration.

对于一个好的WCF源代码尝试:CodePlex,当我开始使用WCF时,它帮助我永无止境。不同的应用程序方案提供了有用的清单,以帮助确保您不会错过配置中的任何步骤。

Good Luck

UPDATE:

Once a channel is faulted, it needs to be recreated as you won't be able to communicate with the service until it's been reset. So add a check to recreate it:

一旦通道出现故障,就需要重新创建,因为在重置之前,您将无法与服务进行通信。所以添加一个检查来重新创建它:

If svc.State = CommunicationState.Faulted Then....

Try remove the svc.Open() line as I've never actually used that. I checked msdn for usage details but got about 2 lines of useless info. Once the service is setup you should be able to communicate with it without having to open it specifically. Not sure that this will actually make a difference though?!

尝试删除svc.Open()行,因为我从未实际使用过它。我检查了msdn的使用细节,但得到了大约2行无用的信息。设置服务后,您应该能够与其进行通信,而无需专门打开它。不确定这实际上会有所作为吗?!

Other things to check: - can you right click on the service and view in browser without problems? - in IIS can you view the certificate in directory security without any issues? - debug up to the point before the service call is made and check the credentials are correctly assigned. - check server event viewer for any info it may have logged with the request (if it's getting that far).

其他要检查的事项: - 您是否可以右键单击服务并在浏览器中查看而不会出现问题? - 在IIS中,您可以在目录安全性中查看证书而不会出现任何问题吗? - 调试到服务调用之前的点,并检查凭据是否正确分配。 - 检查服务器事件查看器,查看它可能记录在请求中的任何信息(如果它已经到了那么远)。

Also, here's some of the exception I trap to determine issues using ex.GetBaseException.GetType:

另外,这里有一些异常,我使用ex.GetBaseException.GetType来确定问题:

ServiceModel.EndpointNotFoundException

  • Server connection problem - either IIS isn't running or invalid server name
  • 服务器连接问题 - IIS未运行或服务器名称无效

ServiceModel.Security.MessageSecurityException

  • Base Exception - ServiceModel.FaultException

    基本异常 - ServiceModel.FaultException

    • "FailedAuthentication" - Bad credentials entered by user

      “FailedAuthentication” - 用户输入的凭据错误

    • "InvalidSecurity" - DB Error - either account has no access to DB, DB name in web config is incorrect, user password has expired in the database

      “InvalidSecurity” - 数据库错误 - 帐户无权访问数据库,Web配置中的数据库名称不正确,用户密码已在数据库中过期

    • "InvalidRequest" - Certificate accessibility issue - check service account has access to certificate

      “InvalidRequest” - 证书可访问性问题 - 检查服务帐户是否可以访问证书

Base Exception - Net.WebException

基本异常 - Net.WebException

  • Unauthorized access 401 - Check anonymous access in IIS is turned on
  • 未经授权的访问401 - 检查IIS中的匿名访问是否已打开

Base Exception - IdentityModel.Tokens.SecurityTokenValidationException

基本异常 - IdentityModel.Tokens.SecurityTokenValidationException

  • No service certificate is assigned in IIS
  • IIS中未分配服务证书

Base Exception - System.ServiceModel.CommunicationException

基本异常 - System.ServiceModel.CommunicationException

  • Identity mismatch with server certificate - eg in dev environment, cert named "localhost" so if you enter PC number or IP address for service you'll see this
  • 身份与服务器证书不匹配 - 例如在开发环境中,证书名为“localhost”,因此如果您输入PC号码或IP地址进行维修,您将看到此信息