WCF
comes with a rich set of security features such as transport level message and
transport with message; each security type has its own advantages and overheads
as well. The best solution is message level security using custom username -
password authentication. After digging in to the net, I found pieces of
information, and with some effort, I implemented a concrete solution which I am
hoping is helpful for others.
I
create a class and name it CustomValidator.cs. We the implement this code
in it:
Note: This
class must be derived from System.IdentityModel.Selectors.UserNamePasswordValidator and
override the Validate method. And to validate the user, use any data
source; in this example, we will use a hard coded value.
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Web;
using
System.IdentityModel.Selectors;
using System.ServiceModel;
namespace Services
{
public class CustomValidator
: UserNamePasswordValidator
{
// This
method validates users. It allows in two users, test1 and test2
// with
passwords 1tset and 2tset respectively.
// This
code is for illustration purposes only and
// must not
be used in a production environment because it is not secure.
/// <summary>
/// userName userId~MachineName
/// </summary>
/// <param
name="userName"></param>
/// <param
name="password"></param>
public
override void
Validate( string userName, string password )
{
if
(null == userName || null
== password)
{
throw
new ArgumentNullException();
}
if
(!( userName == "Amit"
&& password == "Gupta"
))
{
//
This throws an informative fault to the client.
throw
new FaultException("Unknown Username or Incorrect Password");
//
When you do not want to throw an infomative fault to the client,
//
throw the following exception.
//
throw new SecurityTokenException("Unknown Username or Incorrect
Password");
}
}
}
}
|
Creating the service
Add file .svc
file in your project for example is used BusinessPartner.svc
The WCF Service just contains a function Select_Schedule():
[ServiceContract]
public class BusinessPartner
: IBusinessPartner
{
[OperationContract]
public
DateTime Select_Schedule( )
{
try
{
DateTime
dt=DateTime.Now;
return
dt;
}
catch
(Exception ex)
{
throw
new FaultException(Utility.HandleException(ex).ToString());
}
}
}
|
Configuring the Web Service
Modify
the web.config and add following lines in it.
a)
Behavior setting
<behaviors>
<serviceBehaviors>
<behavior name="Services.EZJemsBehaviorValidator">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<serviceCertificate findValue="EzJemshttp" storeLocation="LocalMachine" storeName="My" x509FindType="" />
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Services.CustomValidator,
Services" />
</serviceCredentials>
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
|
b)
Binding setting
<bindings>
<wsHttpBinding>
<binding name="wsHttpBinding_EZJEMS"
closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00"
sendTimeout="00:10:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
|
<service behaviorConfiguration="Services.EZJemsBehaviorValidator" name="Services.BusinessPartner">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_EZJEMS" contract="Services.BusinessPartner" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"
/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/"
/>
</baseAddresses>
</host>
</service>
|
Creating
the site in IIS 7
Open
IIS Manager. Right click Sites and Add Website. Name it as Services, set
Application pool to DeafaultAppPool, and select the physical path.
Now
browse the site & verify the service, it should be up.
The
final step is to create a client to consume the service
Add
the app.config and add following lines in it.
<wsHttpBinding>
<binding name="wsHttpBinding_EZJEMS"
closeTimeout="00:10:00"
openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647"
maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
|
|
<client>
<endpoint address="http://115.111.112.43/EZJEMSServicesOld/BusinessPartner.svc"
binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_EZJEMS"
contract="BusinessPartner.BusinessPartner" name="WSHttpBinding_BusinessPartner">
</endpoint>
</client>
|
The
client application is the desktop application Used service method in your
application using below code:
BusinessPartner.BusinessPartnerClient wsBusinessPartner = new BusinessPartner.BusinessPartnerClient();
wsBusinessPartner.ClientCredentials.UserName.UserName
= "Amit";
wsBusinessPartner.ClientCredentials.UserName.Password
= "Gupta";
|
DateTime dt =
wsBusinessPartner.Select_Schedule(intIdPartner, id_Entity);
|
About Author:
Amit Gupta is technology lead in Systems Plus Pvt. Ltd and keen to resolve challenges using his technical skills. He actively contributes to technology and can be contacted at: amit.gupta@spluspl.com
Nice work Amit.. very informative..
ReplyDelete