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:
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:
In this section, we will explore the best practices to consider when creating applications using WCF.
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.
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 |
.NET |
Named pipe |
Transport |
Yes |
Yes | |
NetNamedPipeBinding |
.NET |
Named pipe |
Transport |
Yes |
Yes |
NetMsmqBinding |
.NET |
MSMQ |
Transport |
Yes |
No |
WS-federation |
HTTP, HTTPS |
Message |
Yes |
No | |
NetPeerTcpBinding |
Peer |
TCP |
Transport |
… |
Yes |
MsmqIntegrationBinding |
MSMQ |
MSMQ |
Transport |
Yes |
… |
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:
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.
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>
Implement message-level security in WCF 4.5 with the following steps:
makecert.exe
tool.<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>
<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!
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 }
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:
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();
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>
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:
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:
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.