Interoperability : WCF Secured Service, .NET Client, SoapUI, Java Client
Hi Guys,
I had a requirement to make a secured WCF service to be hosted in HTTPS and the credentials must be passed with the soap header within the username token tags. So this post is about that.
1) WCF Service
public class MyService : IMyService
{
public ReturnMsg SaveTransaction(Transaction objTransaction, Patient objPatient)
{
System.Data.OracleClient.OracleConnection con = new System.Data.OracleClient.OracleConnection();
System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand();
ReturnMsg objReturnMsg = new ReturnMsg() { QaidNo = -1 };
//The Logics have been removed
try
{
//Validate for Mandatory Fields
if (Validate(objTransaction, objPatient, objReturnMsg))
{
objReturnMsg.QaidNo = 10000;
objReturnMsg.Status = Status.Success;
}
}
catch (Exception ex)
{
objReturnMsg.Status = Status.Failed;
objReturnMsg.Message = ex.Message;
}
finally
{
if (con.State == ConnectionState.Open)
con.Close();
con.Dispose();
}
return objReturnMsg;
}
}
2) WCF Configurations (Web.config)
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="defaultProfile">
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
<serviceMetadata httpGetEnabled="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfService.UsernameValidator, WcfService" />
<clientCertificate>
<authentication certificateValidationMode="None" />
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="basic">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="defaultProfile" name="WcfService.MyService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="basic" name="wsSecureService" contract="WcfService.IMyService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
3) Custom Credential Validator
public class UsernameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
// validate arguments
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password");
// check if the user is not test
if (userName.Trim().ToLower() != WcfService.Properties.Settings.Default.WSSE_USER.Trim().ToLower()
|| password.Trim().ToLower() != WcfService.Properties.Settings.Default.WSSE_PWD.Trim().ToLower())
throw new SecurityTokenException("Unknown username or password");
}
}
4) I created a Self signed certificate in IIS and hosted my Service in HTTPS
How to Create a Self Signed Certificate
.NET Client (Console Application)
1) Add Service Reference to MyService
2) Paste the following code in Program.cs
static void Main(string[] args)
{
//Creating an instance for the client
ServiceReference.MyServiceClient client = new ServiceReference.MyServiceClient();
//Setting the Credentials
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
try
{
//Creating and Filling the Objects which will be passed to method
ClientApplication.ServiceReference.Transaction objTransaction = new ClientApplication.ServiceReference.Transaction();
ClientApplication.ServiceReference.Patient objPatient = new ClientApplication.ServiceReference.Patient();
ClientApplication.ServiceReference.ReturnMsg objReturnMsg = client.SaveTransaction(objTransaction, null);
//If Status is Success
if (objReturnMsg != null && objReturnMsg.Status == ServiceReference.Status.Success)
Console.WriteLine(objReturnMsg.QaidNo);
else
Console.WriteLine(objReturnMsg.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally {
Console.ReadLine();
}
}
3) Output
Soap UI
1) Create a New Soap Project with Soap UI for the hosted WCF service (WSDL)
2) Double click on the created the Request and PasswordText for WSS-Password Type, specify the correct username and password in Request Properties
3) Output
I had a requirement to make a secured WCF service to be hosted in HTTPS and the credentials must be passed with the soap header within the username token tags. So this post is about that.
1) WCF Service
public class MyService : IMyService
{
public ReturnMsg SaveTransaction(Transaction objTransaction, Patient objPatient)
{
System.Data.OracleClient.OracleConnection con = new System.Data.OracleClient.OracleConnection();
System.Data.OracleClient.OracleCommand cmd = new System.Data.OracleClient.OracleCommand();
ReturnMsg objReturnMsg = new ReturnMsg() { QaidNo = -1 };
//The Logics have been removed
try
{
//Validate for Mandatory Fields
if (Validate(objTransaction, objPatient, objReturnMsg))
{
objReturnMsg.QaidNo = 10000;
objReturnMsg.Status = Status.Success;
}
}
catch (Exception ex)
{
objReturnMsg.Status = Status.Failed;
objReturnMsg.Message = ex.Message;
}
finally
{
if (con.State == ConnectionState.Open)
con.Close();
con.Dispose();
}
return objReturnMsg;
}
}
2) WCF Configurations (Web.config)
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="defaultProfile">
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
<serviceMetadata httpGetEnabled="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfService.UsernameValidator, WcfService" />
<clientCertificate>
<authentication certificateValidationMode="None" />
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="basic">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="defaultProfile" name="WcfService.MyService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="basic" name="wsSecureService" contract="WcfService.IMyService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
3) Custom Credential Validator
public class UsernameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
// validate arguments
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password");
// check if the user is not test
if (userName.Trim().ToLower() != WcfService.Properties.Settings.Default.WSSE_USER.Trim().ToLower()
|| password.Trim().ToLower() != WcfService.Properties.Settings.Default.WSSE_PWD.Trim().ToLower())
throw new SecurityTokenException("Unknown username or password");
}
}
4) I created a Self signed certificate in IIS and hosted my Service in HTTPS
How to Create a Self Signed Certificate
.NET Client (Console Application)
1) Add Service Reference to MyService
2) Paste the following code in Program.cs
static void Main(string[] args)
{
//Creating an instance for the client
ServiceReference.MyServiceClient client = new ServiceReference.MyServiceClient();
//Setting the Credentials
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
try
{
//Creating and Filling the Objects which will be passed to method
ClientApplication.ServiceReference.Transaction objTransaction = new ClientApplication.ServiceReference.Transaction();
ClientApplication.ServiceReference.Patient objPatient = new ClientApplication.ServiceReference.Patient();
ClientApplication.ServiceReference.ReturnMsg objReturnMsg = client.SaveTransaction(objTransaction, null);
//If Status is Success
if (objReturnMsg != null && objReturnMsg.Status == ServiceReference.Status.Success)
Console.WriteLine(objReturnMsg.QaidNo);
else
Console.WriteLine(objReturnMsg.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally {
Console.ReadLine();
}
}
3) Output
Soap UI
1) Create a New Soap Project with Soap UI for the hosted WCF service (WSDL)
2) Double click on the created the Request and PasswordText for WSS-Password Type, specify the correct username and password in Request Properties
3) Output
Java Client
1) Create a Java Application using Net Beans
2) Add a Web Service Client for the Java Application
3) Add a SecurityHandler.java class for the project and paste the following code. Here you will specify the username and password.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package serviceclient;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.handler.MessageContext;
import javax.xml.soap.*;
import java.util.*;
import javax.xml.namespace.QName;
/**
*
* @author Sshahim
*/
public final class SecurityHandler implements SOAPHandler<SOAPMessageContext> {
@Override public Set<QName> getHeaders()
{
QName securityHeader = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
"Security");
HashSet<QName> headers = new HashSet<QName>();
headers.add(securityHeader);
return headers;
}
@Override public void close(MessageContext context)
{
}
@Override public boolean handleFault(SOAPMessageContext context)
{
return true;
}
@Override public boolean handleMessage(final SOAPMessageContext msgCtx) {
// Indicator telling us which direction this message is going in
final Boolean outInd = (Boolean) msgCtx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// Handler must only add security headers to outbound messages
if (outInd.booleanValue()) {
try {
// Get the SOAP Envelope
final SOAPEnvelope envelope = msgCtx.getMessage().getSOAPPart().getEnvelope();
// Header may or may not exist yet
SOAPHeader header = envelope.getHeader();
if (header == null)
header = envelope.addHeader();
// Add WSS Usertoken Element Tree
final SOAPElement security = header.addChildElement("Security", "wsse",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
final SOAPElement userToken = security.addChildElement("UsernameToken", "wsse");
userToken.addChildElement("Username", "wsse").addTextNode("username");
userToken.addChildElement("Password", "wsse").addTextNode("password");
} catch (final Exception e) {
return false;
}
}
return true;
}
// Other required methods on interface need no guts
}
4) Open the main java file and paste the following code. This code will bind the created handler before calling the service.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package serviceclient;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.datacontract.schemas._2004._07.wcfservice.ReturnMsg;
import javax.xml.ws.BindingProvider;
import com.sun.xml.wss.XWSSConstants;
import javax.xml.ws.Binding;
import javax.xml.soap.SOAPException;
import java.util.*;
import javax.xml.ws.handler.Handler;
import org.datacontract.schemas._2004._07.wcfservice.Status;
/**
*
* @author Sshahim
*/
public class ServiceClient {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
org.datacontract.schemas._2004._07.wcfservice.Transaction objTransaction =
new org.datacontract.schemas._2004._07.wcfservice.Transaction();
org.datacontract.schemas._2004._07.wcfservice.Patient objPatient =
new org.datacontract.schemas._2004._07.wcfservice.Patient();
ReturnMsg objReturn = saveTransaction(objTransaction, objPatient);
if (objReturn.getStatus() == Status.SUCCESS)
System.out.println(objReturn.getQaidNo());
else
System.out.println(objReturn.getMessage().getValue());
}
private static ReturnMsg saveTransaction(org.datacontract.schemas._2004._07.wcfservice.Transaction objTransaction, org.datacontract.schemas._2004._07.wcfservice.Patient objPatient) {
org.tempuri.MyService service = new org.tempuri.MyService();
org.tempuri.IMyService port = service.getWsSecureService();
final Binding binding = ((BindingProvider)port).getBinding();
List<Handler> handlerList = binding.getHandlerChain();
if (handlerList == null)
handlerList = new ArrayList<Handler>();
handlerList.add(new SecurityHandler());
binding.setHandlerChain(handlerList); // <- important!
return port.saveTransaction(objTransaction, objPatient);
}
}
5) Output
Comments
Post a Comment