WCF Client Using Certificate and Username/Password credentials?

半世苍凉 提交于 2019-12-02 01:03:28

问题


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.

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.

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:

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:


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:

<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:

<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.

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?!

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).

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

ServiceModel.EndpointNotFoundException

  • Server connection problem - either IIS isn't running or invalid server name

ServiceModel.Security.MessageSecurityException

  • Base Exception - ServiceModel.FaultException

    • "FailedAuthentication" - Bad credentials entered by user

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

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

Base Exception - Net.WebException

  • Unauthorized access 401 - Check anonymous access in IIS is turned on

Base Exception - IdentityModel.Tokens.SecurityTokenValidationException

  • No service certificate is assigned in IIS

Base Exception - 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


来源:https://stackoverflow.com/questions/1165078/wcf-client-using-certificate-and-username-password-credentials

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!