Chapter 7. Advanced Features

This is the last chapter of this book, where we will explore some advanced concepts in WCF and Web API.

In this chapter, we will cover the following points:

  • Best practices in using WCF services
  • Best practices in using ASP.NET Web API

Best practices in using WCF

Windows Communication Foundation (WCF) is a unified programming model for building service-oriented applications. WCF provides a powerful framework to design, build, configure, and deploy SOA based applications, where SOA stands for Service Oriented Architecture.

Microsoft released WCF, initially codenamed Indigo, in 2006 as part of .NET Framework 3.0.

WCF enables developers to build secure, reliable, and transacted solutions that integrate across different platforms and provides a high degree of interoperability with existing investments.

The core philosophy of WCF can be boiled down to the following three key concepts commonly known as ABC:

  • Address
  • Binding
  • Contract

In this section, we will explore the best practices to consider when creating applications using WCF.

WCF security issues

In this section, we will explore how we can implement a robust security for our WCF services. We will start our discussion with a brief introduction to WCF bindings.

Bindings

A binding is used to specify the transport channel (HTTP, TCP, pipes, and Message Queuing) and the protocols (Security, Reliability, and Transaction flows). A binding comprises of binding elements, and also includes message encoding elements (text/XML, MTOM, and binary). These binding elements denote how an endpoint communicates with service consumers. WCF provides support for nine built-in bindings. A binding must include at least one transport binding element, one encoding binding element, and one or more other transport protocol bindings, such as security and reliability. Note that the binding information that needs to be specified in the server and the client is different; that is, you have to specify the binding information in the configuration file of your WCF service and also in the configuration file in the WCF service client.

In WCF, the three major sections in WCF configuration scheme are ServiceModel, bindings, and services.

In essence, binding is an attribute of an endpoint, and you can use it to configure the transport protocol, encoding, and security specifications of a service. Now, which is the binding I should use and when? Here's the rule of thumb:

  • WsHttpBinding: You can use this type of binding if you need to expose your service over the Internet.
  • basicHttpBinding: You should select this type of binding if you need to expose your WCF service to legacy clients, such as an ASMX web service. One of the major differences between WsHttpBinding and basicHttpBinding is in message security.
  • WsFederationHttpBinding: This is a special type of of WS binding that offers support for federated security.
  • NetTcpBinding: You can use this type of binding if you need to support WCF clients within an intranet. This is the most optimized and fastest binding available, and supports reliability, transactions, and security. NetTcpBinding provides support for TCP protocol and both the binary as well as encoding methods.
  • NetPeerTcpBinding: This binding provides support for the features of netTcpBinding, and is much more secure for a peer-to-peer environment that uses WCF services.
  • netNamedPipeBinding: This type of binding is a good choice if you need to support WCF clients on the same machine.
  • netMsmqBinding: You can select this type of binding if you need to support disconnected queued calls.
  • wsDualHttpBinding: You can select this type of binding if you would like to provide support for bidirectional communication between the service and the client. This type of binding has all the features of WsHttpBinding; in addition, it provides support for the Duplex Message Exchange Pattern (MEP).

The following table provides a comparison of the bindings in WCF:

Binding

Configuration

Protocol/Transport

Security

Transaction

Duplex

BasicHttpBinding

Basic Profile 1.1

HTTP/HTTPS

None

WSHttpBinding

WS

HTTP, HTTPS, TCP

Message

Yes

WSDualHttpBinding

WS

HTTP, HTTPS

Message

Yes

Yes

NetTcpBinding

.NET

Named pipe

Transport

Yes

Yes

NetNamedPipeBinding

.NET

Named pipe

Transport

Yes

Yes

NetMsmqBinding

.NET

MSMQ

Transport

Yes

No

WSFederationHttpBinding

WS-federation

HTTP, HTTPS

Message

Yes

No

NetPeerTcpBinding

Peer

TCP

Transport

Yes

MsmqIntegrationBinding

MSMQ

MSMQ

Transport

Yes

Note

You can also have Custom binding that allows creating a custom binding using a combination of different binding elements.

WCF security

Confidentiality and integrity of data and information is of utmost importance when you are using WCF services. You create service operations and then expose them to the outer world. There are various ways in which you can secure your WCF services, which are as follows:

  • Using authentication
  • Using authorization
  • Using certificates
  • Using transport level security
  • Using message level security
  • Using token-based security

Additionally, you can provide security in WCF at two levels. You can either provide the security at the transport level or at the message level. Now, there are pros and cons of both these levels.

Transport security is transport dependent. It provides interoperability and improved performance, and should be used when the message that you send is routed through intermediate systems, and both the service and the client are located in an intranet network. However, transport security provides minimum support for credentials when compared to message security.

Message-level security

In message-level security, the credentials of the user are encapsulated with the message that is passed between the server and the client. Message security is suitable when the message needs to be forwarded to other WCF services or routed through intermediate systems. However, message security is slow in comparison to transport security, because of the overhead needed to encrypt and sign every message. Also, message security doesn't support interoperability with older ASMX clients. The credential types that message security supports are Windows, None, Certificate, User Name, and Token.

The following code snippet illustrates how you can implement message-level security by securing a message using the wsHttpBinding binding:

<wsHttpBinding>
  <binding name = "wsHttp">
    <security mode = "Message">
      <message clientCredentialType = "UserToken"/>
    </security>
  </binding>
</wsHttpBinding>

If you are using certificate security in the message security mode, here's how you can secure your WCF services:

<bindings>
  <wsHttpBinding>
    <binding name="wsHttpEndpointBinding">
      <security>      
        <message clientCredentialType="Certificate" />
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

Note

To specify message security protection levels on the interface or operation level, you have to make use of the [ServiceContract(ProtectionLevel)] attribute and set the protection level. You can choose from any of the supported protection levels, that is, None, Sign, and EncryptAndSign.

Implement message-level security in WCF 4.5 with the following steps:

  1. Create certificates for both the server and the client (service provider and service consumer) using the makecert.exe tool.
  2. Using Visual Studio 2013 IDE, create a WCF application.
  3. Specify the necessary binding behavior in the configuration file for the server, as shown in the following code:
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpEndpointBinding">
          <security>
            <message clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    
    <serviceCredentials>
      <clientCertificate>
        <authentication certificateValidationMode="PeerTrust"/>
      </clientCertificate>
      <serviceCertificate findValue="DemoWCFServer"storeLocation="CurrentUser"storeName="My"x509FindType="FindBySubjectName" />
    </serviceCredentials>
  4. Create the WCF client application, and configure the client certificate credentials, as shown in the following code:
    <behaviors>
      <endpointBehaviors>
        <behavior name="CustomBehavior">
          <clientCredentials>
            <clientCertificate findValue="DemoWCFClient" x509FindType="FindBySubjectName" storeLocation="CurrentUser" storeName="My" />
            <serviceCertificate>
              <authentication certificateValidationMode="PeerTrust"/>
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <client>
      <endpoint address="http://localhost:1234/DemoService.svc" binding="wsHttpBinding"
        bindingConfiguration="WSEndpoint" contract="Packt.Services.IDemoService"
        name="WSEndpoint" behaviorConfiguration="CustomBehavior">
        <identity>
          <dns value="DemoWCFServer"/>
        </identity>
      </endpoint>
    </client>

And you are done!

Using the FaultContract attribute

The following code snippet creates an interface with the protection level set to Sign:

[ServiceContract(ProtectionLevel=ProtectionLevel.Sign]
  public interface ISecurityService
  {
    [OperationContract]
    [FaultContract(typeof(FaultDetails))]
    UserLoginHistory GetUserLoginHistory(Int32 userID);       
  }

The FaultContractAttribute class is used to specify one or more SOAP defaults that are returned when a call to a service method encounters an error at runtime. The following code snippet specifies an operation with the protection level set to Sign:

[OperationContract(ProtectionLevel=ProtectionLevel.Sign]

The GetUserLoginInformation service method returns the login history details of the user, whose user ID is passed as a parameter.

The SecurityService class implements the ISecurityService interface (the service contract) and defines the getUserLoginInformation method (the operation contract). The following code snippet shows how the SecurityService class looks:

public class SecurityService : ISecurityService
  {
    public UserLoginHistory GetUserLoginHistory(Int32 userID)
    {
      try
      {
        return new UserLoginHistory { UserID = userID };
      }
      catch 
      { 
        FaultDetails faultObject = new FaultDetails();
        faultObject.FaultID = 1;
        faultObject.FaultMessage = "The User ID you entered is invalid...";

        throw new FaultException<FaultDetails> (faultObject, new FaultReason(faultObject.FaultMessage)); 
      }    
    }
  }

As you can see in the previous code snippet, the GetUserLoginHistory operation contract returns an instance of UserLoginHistory. I've skipped the code to retrieve the user login history for the user ID passed as a parameter.

We would need two message contracts, one for storing the user's login history, and the other for the fault details when a fault exception occurs. The MessageBodyMemberAttribute class belongs to .NET Framework 4.5, and is used to specify that a member is serialized as an element inside the SOAP body.

The following code snippet is the code for the two message contracts that we would need to use:

[MessageContract]
public class UserLoginHistory
{
  [MessageBodyMemberAttribute(Order = 1, ProtectionLevel = ProtectionLevel.None)]
  public Int32 UserID { get; set; }

  [MessageBodyMemberAttribute(Order = 2, ProtectionLevel = ProtectionLevel.Sign)]
  public DateTime LoginTime { get; set; }

  [MessageBodyMemberAttribute(Order = 2, ProtectionLevel = ProtectionLevel.EncryptAndSign)]
  public DateTime Password { get; set; }
}

[MessageContract]
public class FaultDetails
{
  [MessageBodyMemberAttribute(Order = 1, ProtectionLevel = ProtectionLevel.None)]
  public Int32 FaultID
  {
    get;
    set;
  }

  [MessageBodyMemberAttribute(Order = 2, ProtectionLevel = ProtectionLevel.None)]
  public string FaultMessage
  {
    get;
    set;
  }
}

After the service reference has been added, you can consume the service from the client application using the following code:

try
{
  ChannelFactory<ISecurityService> factory =
  new ChannelFactory<ISecurityService>("WSHttpBinding_ISecurityService",
  new EndpointAddress("http://localhost/Packt/SecurityService.svc"));

  ISecurityService proxy = factory.CreateChannel();
  UserLoginHistory userLoginHistoryObj = proxy.GetUserLoginHistory(3);
}
catch (FaultException<FaultDetails> faultExceptionInstance)
{
  //Some code
}

Note

You can turn off security in WCF programmatically. An example of that is as follows:

bindingObject.Security.Mode = SecurityMode.None;

Transport-level security

Transport security works much faster in comparison to message security, and the transport-level security is protocol independent.

The available client credential types you can use when you are implementing the basicHttpBinding or WsHttpBinding binding in the transport security mode include the following:

  • None: No security; security is turned off
  • Basic: Basic authentication works only with the HTTP protocol. Here, the client is authenticated against the active directory
  • Digest: This type of authentication is similar to Basic, but in this option, the credentials are sent as a hash, instead of clear text
  • NTLM: This also works only with the HTTP protocol, and clients are authenticated using Windows accounts
  • Windows: In this option, a Windows token is used to authenticate against the active directory
  • Certificate: In this option, a service is authenticated using the service certificate or an SSL certificate if the protocol in use is HTTP

Implementing transport-level security

The following code snippet illustrates how you can implement transport security using the following code:

NetTcpBinding netTcpBinding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
netTcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
netTcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
Uri adddress = new Uri("net.tcp://Tcp");
ServiceHost serviceHost = new ServiceHost(typeof(SecurityService), adddress);
serviceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My,X509FindType.FindByIssuerName, "Contoso.com"); 
serviceHost.AddServiceEndpoint(typeof(ISecurityService), b, "SecurityService");
serviceHost.Open();
Console.WriteLine("Service Started..........");
Console.Read0Line();

Note

Note that by default, netTcpBinding uses transport security. This implies that you should configure the client credentials to use certificate security.

To implement transport-level security through configuration, you should specify the security mode in the configuration file, as shown in the following code snippet:

<bindings>
  <wsHttpBinding>
    <binding name="TransportSecurity">
      <security mode="Transport">
        <transport clientCredentialType="None"/>
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

Tip

To use netTcpBinding with Windows for transport security, you can use the following code:

<bindings>
  <netTcpBinding>
    <binding name="PacktTcpBinding">
      <security mode="TransportWithMessageCredential" >
        <transport clientCredentialType="Windows" />
        <message clientCredentialType="Certificate" />
      </security>
    </binding>
  </netTcpBinding>
</bindings>

If you use HTTPS protocol, you should change httpGetEnabled to httpsGetEnabled on the service behavior, as shown in the following code snippet:

<behaviors>
  <serviceBehaviors>
    <behavior name="Packt.SecureService.SecurityServiceBehavior">
      <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
      <serviceMetadata httpsGetEnabled="true"/>
      <!-- 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="false"/>
    </behavior>
  </serviceBehaviors>
</behaviors>

The next step is to specify the end points to support the secure communication. Please refer to the following code:

<services>
  <service name="Packt.SecureService.SecurityService" behaviorConfiguration="Packt.SecureService.SecurityServiceBehavior" >
    <!-- Service Endpoints -->
    <endpoint address="http://localhost/Packt/SecurityService.svc" binding="wsHttpBinding" bindingConfiguration="TransportSecurity" contract="Packt.SecureService.ISecurityService"/>
    <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
  </service>
</services>

Now you should host your service in IIS. To do this, right-click on the service project in the solution explorer window, and in the Web tab, select the Use Local IIS Web Server radio button shown in the following screenshot:

Implementing transport-level security

You should also create a virtual directory by clicking on the Create Virtual Directory button, as shown in the preceding screenshot. The next screen looks like this:

Implementing transport-level security

Note

Note that in order to host a service in IIS from Visual Studio IDE, you should open the Visual Studio 2012 IDE in administrator mode.

You can then go to the Internet Services Manager window and associate an SSL certificate to the website. You should also turn on SSL bindings for your website.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset