Home » Enterprise Java » Web Services » JAX-WS » JAX-WS and Secure Java Web Services using UsernameToken : WS-Security with Metro and WSIT

11 Flares Twitter 0 Facebook 1 Google+ 9 LinkedIn 1 Email -- Filament.io 11 Flares ×

JAX-WS and Secure Java Web Services using UsernameToken: WS-Security with Metro and WSIT Example

This tutorial focuses on creating secure JAX-WS web service with UsernameToken WS-Security profile by using Metro and WSIT. You will learn how to create , deploy and run JAX-WS UsernameToken web service security example and UsernameToken jax-ws client. We will also create the service-side username and password validation handlers, Configure the client by providing a callback handler for the username and for the password to send the UsernameToken authentication data.

Metro web service stack

As we discussed earlier, Metro is the reference implementation of JAXB 2.x and JAX-WS 2.x Java™ standards. Metro also has extensive support WS-* SOAP security extensions. The WS-* technologies are related to Web Service Security,Reliability, and Transactions.
WSIT or the Web Service Interoperability Technologies (Also known as Project Tango) provides interoperability between Java Web Services and Microsoft’s WCF( Windows Communication Foundation

Metro is bundled with Java SE 6 and above (but requires the newer 2.2.x version of JAX-WS, this is not provided by default in Java 6 and you’ll need to place the newer jaxws-api.jar in your JDK’s endorsed folder) as well as with application servers such as GlassFish, Oracle Weblogic, JBoss 5.x and above Sun Java App server etc.

Metro implements OASIS WS-Security specifications and W3C Policy Standards. Read Metro specifications implementation details to know more about the different specification versions implementation.
The bootstrapping and configuration specifications deals with accessing web service URLs , WSDLs and provides information that are required to build web service clients. Message Optimization Specifications deals with optimized communication between two web service endpoints.

WS-ReliableMessaging specifications deal with system’s ability to deliver messages from point A to point B regardless of network errors and WS-Security specifications targets to secure communication between two web service endpoints.

Related Articles:

In this tutorial, we develop a sample JAX-WS service and client application which include WS-Security with the UsernameToken profile.

Usernametoken ws-security using Metro and WSIT : JAX-WS UsernameToken web service and client examples.

Here we focus on a simple plain-text UsernameToken example using Metro. We will see more secure encrypted versions in the next tutorials.
UsernameToken provides a standard way of representing a username and password pair with WS-Security. Please note that, the plain text username and password is used in production only when it is combined with SSL or WS-Security encryption. That means metro requires either transport-level encryption ( SSL) or other message-level encryption to be in use while using this UsernameToken profile. User Name token will be part of SOAP Header rather than HTTP header

Points to remember when using WS-Security with the UsernameToken profile.

  • The custom extensions used to configure WSIT user information differ in the XML namespace on the client and server sides. For client the namespace is “ http://schemas.sun.com/2006/03/wss/client” and for server the XML namespace is “http://schemas.sun.com/2006/03/wss/server”.
  • The policy namespace refers http://java.sun.com/xml/ns/wsit/policy
  • The WS-Security/UsernameTokens can be configured either directly as part of WSDL document using WS-SecurityPolicy assertions or using separate configuration files. The service side configuration file is named as “wsit-*.xml” and client side configuration file follows the “wsit-client.xml” naming conventions.
  • Metro provides two options for server side authentication when UserNameToken profile is used. It can be either container based or via a validation handler class.
    • If it is container based you need to configure the realm, for example if you are using Tomcat container you can configure tomcat-users.xml file to include user credentials to validate the tokens.
    • You can also use validation handler classes as part of the application instead of relying on container realm based default validations.

To try the example below, you need to have IDE such as Eclipse, JDK version 7 (Metro requires the newer 2.2.x version of JAX-WS (jaxws-api.jar) available in Java 7. This is not provided by default in Java 6 and you’ll need to place the newer jaxws-api.jar in your JDK’s endorsed folder) , Metro distribution and Tomcat server 6 or above.

The WS-SecurityPolicy configuration is not very easy and it is recommended to use some IDE such as NetBeans to create them. If you have Netbeans and Glassfish installed, the JAX-WS web service development is pretty easy. You can read the WSIT Netbeans tutorial to learn more.
In this tutorial, I used eclipse and Tomcat so that we can create web service, client and configuration files manually without generating them and deploy and test them on Tomcat.

Create the SEI and the JAX-WS Web Service Implementation class.

The SEI is created and annotated as a web service. It defines three methods. These methods allow the clients to upload images, download images and a simple greetCustomer method.

/*
 * @(#)MyWebServiceIntf.java
 * @author Binu George
 * Globinch.com
 * copyright http://www.globinch.com. All rights reserved.
 */
package com.globinch.service;

import java.awt.Image;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
 * The SEI
 * @author Binu George
 * @since 2013
 * @version 1.0
 */
@WebService(name="MyWebService")
public interface MyWebServiceIntf {

	/**
	 * Web service operation
	 */
	@WebMethod(operationName = "greetCustomer")
	public String greetCustomer(@WebParam(name = "parameter") String parameter);

	@WebMethod(operationName = "retrieveImage")
	public Image retrieveImage(@WebParam(name = "name") String name);

	@WebMethod(operationName = "uploadImage")
	public String uploadImage(@WebParam(name = "file") Image file,
			@WebParam(name = "name") String name);

}

The implementation class is below.

 /*
 * @(#)MyWebService.java
 * @author Binu George
 * Globinch.com
 * copyright http://www.globinch.com. All rights reserved.
 */
package com.globinch.service;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.jws.WebService;
import javax.xml.ws.WebServiceException;

/**
 * The web service implementation class.
 * @author Binu George
 * @since 2013
 * @version 1.0
 */
@WebService()
public class MyWebService implements MyWebServiceIntf {
	 final static String PATH = "D:\\mtomtest\\upload\\";

    /* (non-Javadoc)
	 * @see com.globinch.service.MyWebServiceIntf#greetCustomer(java.lang.String)
	 */
    @Override
    public String greetCustomer(String parameter) {
    	
        return "Hello..." + parameter;
    }
 	/* (non-Javadoc)
	 * @see com.globinch.service.MyWebServiceIntf#retrieveImage(java.lang.String)
	 */
 	@Override
	public Image retrieveImage(String name) {
 		
		try {
			// Create a file object with file name and read the image
			File image = new File(PATH + name);
			return ImageIO.read(image);
		} catch (IOException e) {
			throw new WebServiceException("Download Failed");
		}
	}
	/* (non-Javadoc)
	 * @see com.globinch.service.MyWebServiceIntf#uploadImage(java.awt.Image, java.lang.String)
	 */
	@Override
	public String uploadImage(Image file, String name) {
		
		if (file != null) {
			try {
				File image = new File(PATH + name);
				ImageIO.write((BufferedImage) file, "jpg", image);
			} catch (IOException e) {
				throw new WebServiceException("Upload Failed");
			}
			return "Upload Successful";
		}
		throw new WebServiceException("No data to upload.");
	}
  }

Create the service-side username and password validation handler.

In this article we are using plane text username and passwords. They are not encrypted. The PlainTextPasswordRequest represents the validation request when the password in the username token is in plain text.
PasswordValidationCallback is a callback for Password validation. This callback may be used by an authentication module to employ the password validation facilities of its containing runtime. This Callback would typically be called by a ServerAuthModule during validateRequest processing. In our case, in WSIT configuration file we add the ValidatorConfiguration and indicate our custom validator class name which implements PasswordValidationCallback.PasswordValidator. You can use this validator class to authenticate customers against your own services such as database or external services.

/*
 * @(#) MyServicePasswordValidator.java
 * @author Binu George
 * Globinch.com
 * copyright http://www.globinch.com. All rights reserved.
 */
package com.globinch.service;

import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
/**
 * The plain text password validation class.
 * @author Binu George
 * @since 2013
 * @version 1.0
 */
public class MyServicePasswordValidator implements
        PasswordValidationCallback.PasswordValidator {
	
    public boolean validate(PasswordValidationCallback.Request request)
            throws PasswordValidationCallback.PasswordValidationException {

        PasswordValidationCallback.PlainTextPasswordRequest plainTextRequest 
            = (PasswordValidationCallback.PlainTextPasswordRequest) request;
          if ("myuserid".equals(plainTextRequest.getUsername())
                && "mypassword".equals(plainTextRequest.getPassword())) {
            return true;
        
        }else{
        	throw new PasswordValidationCallback.PasswordValidationException("Invalid credentials provided. Authentication failed");
        }
        
        //return false;
    }
}

sun-jaxws.xml server side configuration file

Now we need a sun-jaxws.xml configuration file. As we already discussed in other articles, sun-jaxws.xml is a proprietary deployment descriptor needed when web services are deployed as a standard WAR archive on a non-Java EE5 servlet container using the SUN’s reference implementation.

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
 The jax-ws xml configuration file for tomcat servlet container.
 www.Globinch.com
 -->
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
    <endpoint name="MyWebService"
        implementation="com.globinch.service.MyWebService" 
        url-pattern="/"
        wsdl-location="" enable-mtom="true">
    </endpoint>

</endpoints>

If wsdl-location isn’t specified, JAX-WS will create and publish a new WSDL. When the service is developed from Java, it is recommended to omit this attribute.

The web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">

	<display-name>MyWebService</display-name>
	<description>my Web Service</description>
	<listener>
		<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
	</listener>
	<servlet>
		<servlet-name>MyWebServicePort</servlet-name>
		<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>MyWebServicePort</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	<session-config>
		<session-timeout>60</session-timeout>
	</session-config>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
</web-app>

Configuring usernameToken Security Policy in wsit-service.xml/WSDL file

It is important to note that to implement simple plain-text UsernameToken example on Metro, you need to include appropriate WS-Policy/WS-SecurityPolicy configuration in the WSDL service definition file.
You can either generate the wsdl file using the wsgen tool which is available as part of JDK distribution or you can use the WDL generated by the JAXWS runtime when you deploy your service.
The wsgen tool reads a web service endpoint implementation class (SEI annotated with @WebService annotation) and generates JAX-WS portable artifacts used in JAX-WS web services.
The following command will generate a SOAP 1.2 WSDL

wsgen -wsdl:Xsoap1.2 -d directory -cp myclasspath yourclassfile 

Once you have the WSDL file you can create the server side WSIT configuration file. As we mentioned earlier the server side WSIt configuration file is usually named as “wsit-package.service.xml“, where “package” is the service package and “service” is the service name. This file defines which WSIT technologies are enabled in the web service. Usually you can include these policy information as part of WSDL itself. You can include the policy refrence in <wsdl:binding> section as below.

<binding name="MyWebServicePortBinding" type="tns:MyWebService">
	<wsp:PolicyReference URI="#MyWebServicePortBindingPolicy" />
…..
</binding>

And the policy elements are added as below.

<wsp:Policy wsu:Id="MyWebServicePortBindingPolicy">
  <wsp:ExactlyOne>
    <wsp:All>
	<wsaws:UsingAddressing xmlns:wsaws="http://www.w3.org/2006/05/addressing/wsdl" />
		<sp:SupportingTokens>
			<wsp:Policy>
				<sp:UsernameToken
					sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">				
                         <wsp:Policy>
						<sp:WssUsernameToken10 />
					</wsp:Policy>
				</sp:UsernameToken>
			</wsp:Policy>
		</sp:SupportingTokens>
		<wsss:ValidatorConfiguration
wspp:visibility="private" xmlns:wsss="http://schemas.sun.com/2006/03/wss/server"
			xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy">
			<wsss:Validator name="usernameValidator"
				classname="com.globinch.service.MyServicePasswordValidator" />
			</wsss:ValidatorConfiguration>
	</wsp:All>
   </wsp:ExactlyOne>
</wsp:Policy>

You can both include this as part of wsdl and provide the wsdl-location element in sun-jaxws.xml or you can include the above configurations in wsit-package.service.xml.
In our example we have created wsit-com.globinch.service.MyWebService.xml and the content is as follows.

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="MyWebServiceService"
	targetNamespace="http://com.globinch.service/" 
xmlns:tns="http://com.globinch.service/"
xmlns:wsam=http://www.w3.org/2007/05/addressing/metadata xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
	xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
	xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsaws="ttp://www.w3.org/2005/08/addressing"
xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy"
	xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
	<message name="uploadImage">
		<part name="parameters" element="tns:uploadImage" />
	</message>
	<message name="uploadImageResponse">
		<part name="parameters" element="tns:uploadImageResponse" />
	</message>
	<message name="greetCustomer">
		<part name="parameters" element="tns:greetCustomer" />
	</message>
	<message name="greetCustomerResponse">
		<part name="parameters" element="tns:greetCustomerResponse" />
	</message>
	<message name="retrieveImage">
		<part name="parameters" element="tns:retrieveImage" />
	</message>
	<message name="retrieveImageResponse">
		<part name="parameters" element="tns:retrieveImageResponse" />
	</message>
	<portType name="MyWebService">
		<operation name="uploadImage">
<input wsam:Action="http://service.globinch.com/MyWebService/uploadImageRequest"
				message="tns:uploadImage" />
			<output
			wsam:Action="http://service.globinch.com/MyWebService/uploadImageResponse"
				message="tns:uploadImageResponse" />
		</operation>
		<operation name="greetCustomer">
			<input
			wsam:Action="http://service.globinch.com/MyWebService/greetCustomerRequest"
				message="tns:greetCustomer" />
			<output
			wsam:Action="http://service.globinch.com/MyWebService/greetCustomerResponse"
				message="tns:greetCustomerResponse" />
		</operation>
		<operation name="retrieveImage">
			<input
			wsam:Action="http://service.globinch.com/MyWebService/retrieveImageRequest"
				message="tns:retrieveImage" />
			<output
			wsam:Action="http://service.globinch.com/MyWebService/retrieveImageResponse"
				message="tns:retrieveImageResponse" />
		</operation>
	</portType>
	<binding name="MyWebServicePortBinding" type="tns:MyWebService">
		<strong><wsp:PolicyReference
			URI="#MyWebServicePortBinding_MTOM_Policy-MyWebServicePortBinding_MTOM_Policy" />
		<wsp1_2:PolicyReference URI="#MyWebServicePortBindingPolicy" /></strong>
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
			style="document" />
		<operation name="uploadImage">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
		<operation name="greetCustomer">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
		<operation name="retrieveImage">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
	</binding>
	<service name="MyWebServiceService">
		<port name="MyWebServicePort" binding="tns:MyWebServicePortBinding" />
	</service>
	<wsp1_2:Policy wsu:Id="MyWebServicePortBindingPolicy">
		<wsp:ExactlyOne>
			<wsp1_2:All>
				<wsaws:UsingAddressing xmlns:wsaws="http://www.w3.org/2006/05/addressing/wsdl" />
				<sp:SupportingTokens>
					<wsp1_2:Policy>
						<sp:UsernameToken
			sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
							<wsp:Policy>
								<sp:WssUsernameToken10 />
							</wsp:Policy>
						</sp:UsernameToken>
					</wsp1_2:Policy>
				</sp:SupportingTokens>
				<wsss:ValidatorConfiguration
					wspp:visibility="private" xmlns:wsss="http://schemas.sun.com/2006/03/wss/server"
					xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy">
					<wsss:Validator name="usernameValidator"
						classname="com.globinch.service.MyServicePasswordValidator" />
				</wsss:ValidatorConfiguration>
				
			</wsp1_2:All>
		</wsp:ExactlyOne>
	</wsp1_2:Policy>
	<wsp:Policy
		wsu:Id="MyWebServicePortBinding_MTOM_Policy-MyWebServicePortBinding_MTOM_Policy">
		<ns1:OptimizedMimeSerialization
			xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization"
			wsp:Optional="true" />
	</wsp:Policy>
</definitions>

The location of the WSIT configuration file should be either inside META-INF or under WEB-INF/Classes folder. The JAX-WS runtime will load the site files during deployment. You will see messages similar to the below in the server log files.

INFO: WSSERVLET12: JAX-WS context listener initializing
May 14, 2013 3:32:18 PM [com.sun.xml.ws.policy.parser.PolicyConfigParser]  parse
INFO: WSP5018: Loaded WSIT configuration from file: jndi:/localhost/MyService/WEB-INF/wsit-com.globinch.service.MyWebService.xml.

Now our service is ready for deployment. Once you deploy the service you can access the WSDL file from the URL similar to the below.
http://{host}:{port}/MyService/?wsdl
You will see the WSDL as follows

<!--
		Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
		Metro/2.2.1-1 (tags/2.2.1-1-7267; 2012-08-30T14:04:51+0000)
		JAXWS-RI/2.2.7 JAXWS/2.2 svn-revision#unknown.
	-->
	<!--
		Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
		Metro/2.2.1-1 (tags/2.2.1-1-7267; 2012-08-30T14:04:51+0000)
		JAXWS-RI/2.2.7 JAXWS/2.2 svn-revision#unknown.
	-->
<definitions
	xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
	xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
	xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tns="http://service.globinch.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://service.globinch.com/"
	name="MyWebServiceService">
	<wsp:Policy
		wsu:Id="MyWebServicePortBinding_MTOM_Policy-MyWebServicePortBinding_MTOM_Policy">
		<ns1:OptimizedMimeSerialization
			xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization"
			wsp:Optional="true" />
	</wsp:Policy>
	<wsp1_2:Policy xmlns:wsapw3c="http://www.w3.org/2006/05/addressing/wsdl"
		xmlns:ssp="http://schemas.sun.com/2006/03/wss/server" xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"
		xmlns:sunwsp="http://java.sun.com/xml/ns/wsit/policy" wsu:Id="MyWebServicePortBindingPolicy">
		<sp:SupportingTokens>
			<wsp1_2:Policy>
				<sp:UsernameToken
					sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
					<wsp1_2:Policy>
						<sp:WssUsernameToken10 />
					</wsp1_2:Policy>
				</sp:UsernameToken>
			</wsp1_2:Policy>
		</sp:SupportingTokens>
		<wsapw3c:UsingAddressing />
	</wsp1_2:Policy>
	<types>
		<xsd:schema>
			<xsd:import namespace="http://service.globinch.com/"
				schemaLocation="http://localhost:8080/MyService/?xsd=1" />
		</xsd:schema>
	</types>
	<message name="uploadImage">
		<part name="parameters" element="tns:uploadImage" />
	</message>
	<message name="uploadImageResponse">
		<part name="parameters" element="tns:uploadImageResponse" />
	</message>
	<message name="greetCustomer">
		<part name="parameters" element="tns:greetCustomer" />
	</message>
	<message name="greetCustomerResponse">
		<part name="parameters" element="tns:greetCustomerResponse" />
	</message>
	<message name="retrieveImage">
		<part name="parameters" element="tns:retrieveImage" />
	</message>
	<message name="retrieveImageResponse">
		<part name="parameters" element="tns:retrieveImageResponse" />
	</message>
	<portType name="MyWebService">
		<operation name="uploadImage">
			<input wsam:Action="http://service.globinch.com/MyWebService/uploadImageRequest"
				message="tns:uploadImage" />
			<output
				wsam:Action="http://service.globinch.com/MyWebService/uploadImageResponse"
				message="tns:uploadImageResponse" />
		</operation>
		<operation name="greetCustomer">
			<input
				wsam:Action="http://service.globinch.com/MyWebService/greetCustomerRequest"
				message="tns:greetCustomer" />
			<output
				wsam:Action="http://service.globinch.com/MyWebService/greetCustomerResponse"
				message="tns:greetCustomerResponse" />
		</operation>
		<operation name="retrieveImage">
			<input
				wsam:Action="http://service.globinch.com/MyWebService/retrieveImageRequest"
				message="tns:retrieveImage" />
			<output
				wsam:Action="http://service.globinch.com/MyWebService/retrieveImageResponse"
				message="tns:retrieveImageResponse" />
		</operation>
	</portType>
	<binding name="MyWebServicePortBinding" type="tns:MyWebService">
		<wsp:PolicyReference
			URI="#MyWebServicePortBinding_MTOM_Policy-MyWebServicePortBinding_MTOM_Policy" />
		<wsp1_2:PolicyReference URI="#MyWebServicePortBindingPolicy" />
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
			style="document" />
		<operation name="uploadImage">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
		<operation name="greetCustomer">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
		<operation name="retrieveImage">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
	</binding>
	<service name="MyWebServiceService">
		<port name="MyWebServicePort" binding="tns:MyWebServicePortBinding">
			<soap:address location="http://localhost:8080/MyService/" />
		</port>
	</service>
</definitions>

Now the JAX-WS UsernameToken security web service is deployed and ready to test.

Create JAX-WS UsernameToken client

When you create JAX-WS client application using metro you need to have the following files.

  • The service proxy classes
  • {wsdl file name}.xml
  • wsit-client.xml
  • Service client classes.
  • CallbackHandler class.

The first step in creating the web service client is to generate the proxy artifacts using wsimport utility. Ofcourse you can always use the IDE tools for generating these client side files.
A sample command is given below

wsimport -keep -XadditionalHeaders http://localhost:8080/MyService/?wsdl

We will create the client classes using the above generated classes. In this application I have created a ServiceInvoker class which invokes the services such as upload image, retrieve image and greet customer. Please note that the client service invoker just picks the image from a physical location and send it to the “uploadImage” service of MyWebService”. Similar way the client also requests an image from server and the server sends back the image to the client. The client saves the image in a physical location.

UsernameToken jax-ws client Example

/*
 * @(#)Client.java
 * @author Binu George
 * Globinch.com
 * copyright http://www.globinch.com. All rights reserved.
 */
package com.globinch.client;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;

import com.globinch.service.MyWebService;
/**
 * The web service client service invoker class.
 * @author Binu George
 * @since 2013
 * @version 1.0
 */
public class Client {
	private static URL url = null;
	private static Service service = null;
	private static final String PATH = "D:\\mtomtest\\download\\";
	
	public Client() {
		try {
			url = new URL("https://localhost:8443/MyService/?wsdl");
			QName qname = new QName("http://service.globinch.com/",
					"MyWebServiceService");
			service = Service.create(url, qname);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
       /**
	 * Upload image
	 * @return String
	 */
	public String uploadImage() {
		try {
			MyWebService myService = service.getPort(MyWebService.class);
			File file = new File(PATH + "11841.jpg");
			BufferedImage imageToUpload = null;
			try {
				imageToUpload = ImageIO.read(file);
			} catch (IOException e) {
				e.printStackTrace();
			}
			double d = Math.random();
			byte[] imageInByte = null;
			try {
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				ImageIO.write(imageToUpload, "jpg", baos);
				baos.flush();
				imageInByte = baos.toByteArray();
				baos.close();
			} catch (IOException e) {
				System.out.println(e.getMessage());
			}
			// now upload the image
			myService.uploadImage(imageInByte, "ImageFromClient" + d + ".jpg");
			String imageToDownload = d+"";
						
			return imageToDownload;
		} catch (Exception e) {

			e.printStackTrace();
		}
		return null;
	}
	/**
	 * Download the uploaded image
	 * @param imageName
	 * @return String
	 */
	public String downloadImage(String imageName) {
		try {
			MyWebService myService = service.getPort(MyWebService.class);
			// lets now download the uploaded image
			byte[] downloadedImage = myService
					.retrieveImage("ImageFromClient" + imageName + ".jpg");
			File imageFromServer = new File(PATH + "ImageFromServer" + imageName
					+ ".jpg");
			try {
				FileOutputStream fileOuputStream = new FileOutputStream(
						imageFromServer);
				fileOuputStream.write(downloadedImage);
				fileOuputStream.close();
				
			} catch (IOException e) {
				e.printStackTrace();
			}
			return "success";
		} catch (Exception e) {

			e.printStackTrace();
		}
		return null;

	}
	/**
	 * Greet the customer
	 * @param name
	 * @return String
	 */
	public String greet(String name) {
		try {
			MyWebService myService = service.getPort(MyWebService.class);
			String message = myService.greetCustomer(name);
			System.out.println(message);
			return message;

		} catch (Exception exception) {
			exception.printStackTrace();
		}
		return "empty string";
	}

	public static void main(String[] args) {

		try {
			new Client().greet("Some user");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Client side WSIT configuration file

The WSIT policy configuration needs to be applied using the wsit configuration file. On the client side, a file with the name of wsit-client.xml is used for the WSIT configuration. You also need to create a service xml file. The name for the service xml file is usually will be {wsdl file name}.xml.
The wsit-client.xml imports the service xml file. In effect these two files are simply a copy of the service WSDL with its wsp:Policy element modified to provide client-side configuration information such as callback handler etc.

MyWebServiceService.xml

<?xml version="1.0" encoding="UTF-8"?> 
<definitions
	xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
	xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
	xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tns="http://service.globinch.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://service.globinch.com/"
	xmlns:sc="http://schemas.sun.com/2006/03/wss/client"
	xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy"
	name="MyWebServiceService">
	<wsp:Policy
		wsu:Id="MyWebServicePortBinding_MTOM_Policy-MyWebServicePortBinding_MTOM_Policy">
		<ns1:OptimizedMimeSerialization
			xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization"
			wsp:Optional="true" />
	</wsp:Policy>
<wsp1_2:Policy wsu:Id="MyWebServicePortBindingPolicy">
    <wsp:ExactlyOne>
      <wsp:All>
          <sc:CallbackHandlerConfiguration wspp:visibility="private">
          <sc:CallbackHandler classname="com.globinch.client.MyCallbackHandler" name="usernameHandler"/>
          <sc:CallbackHandler classname="com.globinch.client.MyCallbackHandler" name="passwordHandler"/>
        </sc:CallbackHandlerConfiguration>
      </wsp:All>
    </wsp:ExactlyOne>
  </wsp1_2:Policy>
	<types>
		<xsd:schema>
			<xsd:import namespace="http://service.globinch.com/"
				schemaLocation="http://localhost:8080/MyService/?xsd=1" />
		</xsd:schema>
	</types>
	<message name="uploadImage">
		<part name="parameters" element="tns:uploadImage" />
	</message>
	<message name="uploadImageResponse">
		<part name="parameters" element="tns:uploadImageResponse" />
	</message>
	<message name="greetCustomer">
		<part name="parameters" element="tns:greetCustomer" />
	</message>
	<message name="greetCustomerResponse">
		<part name="parameters" element="tns:greetCustomerResponse" />
	</message>
	<message name="retrieveImage">
		<part name="parameters" element="tns:retrieveImage" />
	</message>
	<message name="retrieveImageResponse">
		<part name="parameters" element="tns:retrieveImageResponse" />
	</message>
	<portType name="MyWebService">
		<operation name="uploadImage">
			<input wsam:Action="http://service.globinch.com/MyWebService/uploadImageRequest"
				message="tns:uploadImage" />
			<output
				wsam:Action="http://service.globinch.com/MyWebService/uploadImageResponse"
				message="tns:uploadImageResponse" />
		</operation>
		<operation name="greetCustomer">
			<input
				wsam:Action="http://service.globinch.com/MyWebService/greetCustomerRequest"
				message="tns:greetCustomer" />
			<output
				wsam:Action="http://service.globinch.com/MyWebService/greetCustomerResponse"
				message="tns:greetCustomerResponse" />
		</operation>
		<operation name="retrieveImage">
			<input
				wsam:Action="http://service.globinch.com/MyWebService/retrieveImageRequest"
				message="tns:retrieveImage" />
			<output
				wsam:Action="http://service.globinch.com/MyWebService/retrieveImageResponse"
				message="tns:retrieveImageResponse" />
		</operation>
	</portType>
	<binding name="MyWebServicePortBinding" type="tns:MyWebService">
		<wsp:PolicyReference
			URI="#MyWebServicePortBinding_MTOM_Policy-MyWebServicePortBinding_MTOM_Policy" />
		<wsp1_2:PolicyReference URI="#MyWebServicePortBindingPolicy" />
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
			style="document" />
		<operation name="uploadImage">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
		<operation name="greetCustomer">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
		<operation name="retrieveImage">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
	</binding>
	<service name="MyWebServiceService">
		<port name="MyWebServicePort" binding="tns:MyWebServicePortBinding">
			<soap:address location="http://localhost:8080/MyService/" />
		</port>
	</service>
</definitions>

wsit-client.xml

<?xml version="1.0" encoding="UTF-8"?>
<definitions
  xmlns="http://schemas.xmlsoap.org/wsdl/"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" name="mainclientconfig">
  <import location="MyWebServiceService.xml" namespace="http://service.globinch.com/"/>
</definitions>

CallbackHandler to manage username and password at client side

An application implements a CallbackHandler and passes it to underlying security services so that they may interact with the application to retrieve specific authentication data, such as usernames and passwords. For example in this particular case the underlying service needs a username and password to authenticate a user, it uses a NameCallback and PasswordCallback.
In MyWebServiceService.xml the callback handler is configured in the <sc:CallbackHandlerConfiguration> section. The callback handler must implement javax.security.auth.callback.CallbackHandler interface.

* @(#)MyWebServiceCallbackHandler.java
 * @author Binu George
 * Globinch.com
 * copyright http://www.globinch.com. All rights reserved.
 */
package com.globinch.client;

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
/**
 * The callback handler class to manage username and password.
 * @author Binu George
 * @since 2013
 * @version 1.0
 */
public class MyWebServiceCallbackHandler implements CallbackHandler {
   
	   public void handle(Callback[] callbacks) throws IOException,
	      UnsupportedCallbackException {
	         for (int i = 0; i < callbacks.length; i++) {
	            if (callbacks[i] instanceof NameCallback) {
	               NameCallback nc = (NameCallback) callbacks[i];
	               nc.setName("myuserid");
	            } else if (callbacks[i] instanceof PasswordCallback) {
	               PasswordCallback pc = (PasswordCallback) callbacks[i];
	               pc.setPassword("mypassword".toCharArray());
	            } else {
	               throw new UnsupportedCallbackException(callbacks[i],
	                  "Unrecognized Callback");
	            }
	         }
	   }
	}

You can create a web project to test the above example. You can add a servlet component to invoke the Client.java methods along with a JSP page to upload and download images.

As we mentioned in the beginning of the article, since the username and password is being sent as plain text ensure that the endpoint URL is using HTTPS/SSL to encrypt the messages if deployed in production.

References:

Incoming search terms:

11 Flares Twitter 0 Facebook 1 Google+ 9 LinkedIn 1 Email -- Filament.io 11 Flares ×

About Binu George

15 thoughts on “JAX-WS and Secure Java Web Services using UsernameToken : WS-Security with Metro and WSIT

  1. Alessandro Mancini says:

    Hi George,
    We tried your code with Metro version 2.2.1 on the Winstone Servlet Container.
    While the test works with the correct user credentials, when we provide a wrong password to the WS-Security protected Service we get the following error:

    SEVERE: WSS1408: UsernameToken Authentication Failed
    giu 14, 2013 3:20:37 PM com.sun.xml.wss.jaxws.impl.SecurityServerTube processRequest
    SEVERE: WSSTUBE0025: Error in Verifying Security in the Inbound Message.
    com.sun.xml.wss.impl.WssSoapFaultException: Authentication of Username Password Token Failed
    at com.sun.xml.ws.security.opt.impl.util.SOAPUtil.newSOAPFaultException(SOAPUtil.java:133)
    at com.sun.xml.ws.security.opt.impl.incoming.UsernameTokenHeader.validate(UsernameTokenHeader.java:173)
    at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.handleSecurityHeader(SecurityRecipient.java:348)
    at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.cacheHeaders(SecurityRecipient.java:283)
    at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.validateMessage(SecurityRecipient.java:225)
    at com.sun.xml.wss.jaxws.impl.SecurityTubeBase.verifyInboundMessage(SecurityTubeBase.java:452)
    at com.sun.xml.wss.jaxws.impl.SecurityServerTube.processRequest(SecurityServerTube.java:206)
    at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:629)
    at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:588)
    at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:573)
    at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:470)
    at com.sun.xml.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:243)
    at com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:471)
    at com.sun.xml.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:244)
    at com.sun.xml.ws.transport.http.servlet.ServletAdapter.handle(ServletAdapter.java:135)
    at com.sun.xml.ws.transport.http.servlet.WSServletDelegate.doGet(WSServletDelegate.java:129)
    at com.sun.xml.ws.transport.http.servlet.WSServletDelegate.doPost(WSServletDelegate.java:160)
    at com.sun.xml.ws.transport.http.servlet.WSServlet.doPost(WSServlet.java:75)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:121)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:45)
    at winstone.ServletConfiguration.execute(ServletConfiguration.java:249)
    at winstone.RequestDispatcher.forward(RequestDispatcher.java:335)
    at winstone.RequestHandlerThread.processRequest(RequestHandlerThread.java:244)
    at winstone.RequestHandlerThread.run(RequestHandlerThread.java:150)
    at java.lang.Thread.run(Thread.java:722)

    Did you try your code with a wrong username/password?
    We also tried to run the code with previous versions of the Metro libraries, but to no avail.

    We suspect that the WS-Security fault processing may be broken.
    Could you help us in this matter?
    Thanks in advance.

    Alessandro.

  2. Alessandro Mancini says:

    Hi George,
    We tried your code with Metro version 2.2.1 on the Winstone Servlet Container.
    While the test works with the correct user credentials, when we provide a wrong password to the WS-Security protected Service we get the following error:

    SEVERE: WSS1408: UsernameToken Authentication Failed
    giu 14, 2013 3:20:37 PM com.sun.xml.wss.jaxws.impl.SecurityServerTube processRequest
    SEVERE: WSSTUBE0025: Error in Verifying Security in the Inbound Message.
    com.sun.xml.wss.impl.WssSoapFaultException: Authentication of Username Password Token Failed
    at com.sun.xml.ws.security.opt.impl.util.SOAPUtil.newSOAPFaultException(SOAPUtil.java:133)
    at com.sun.xml.ws.security.opt.impl.incoming.UsernameTokenHeader.validate(UsernameTokenHeader.java:173)
    at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.handleSecurityHeader(SecurityRecipient.java:348)
    at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.cacheHeaders(SecurityRecipient.java:283)
    at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.validateMessage(SecurityRecipient.java:225)
    at com.sun.xml.wss.jaxws.impl.SecurityTubeBase.verifyInboundMessage(SecurityTubeBase.java:452)
    at com.sun.xml.wss.jaxws.impl.SecurityServerTube.processRequest(SecurityServerTube.java:206)
    at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:629)
    at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:588)
    at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:573)
    at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:470)
    at com.sun.xml.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:243)
    at com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:471)
    at com.sun.xml.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:244)
    at com.sun.xml.ws.transport.http.servlet.ServletAdapter.handle(ServletAdapter.java:135)
    at com.sun.xml.ws.transport.http.servlet.WSServletDelegate.doGet(WSServletDelegate.java:129)
    at com.sun.xml.ws.transport.http.servlet.WSServletDelegate.doPost(WSServletDelegate.java:160)
    at com.sun.xml.ws.transport.http.servlet.WSServlet.doPost(WSServlet.java:75)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:121)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:45)
    at winstone.ServletConfiguration.execute(ServletConfiguration.java:249)
    at winstone.RequestDispatcher.forward(RequestDispatcher.java:335)
    at winstone.RequestHandlerThread.processRequest(RequestHandlerThread.java:244)
    at winstone.RequestHandlerThread.run(RequestHandlerThread.java:150)
    at java.lang.Thread.run(Thread.java:722)

    Did you try your code with a wrong username/password?
    We also tried to run the code with previous versions of the Metro libraries, but to no avail.

    We suspect that the WS-Security fault processing may be broken.
    Could you help us in this matter?
    Thanks in advance.

    Alessandro.

  3. Binu George says:

    Hi Mancini,
    Could you please check your validator class? Is it typecasting the request properly?

  4. Binu George says:

    Hi Mancini,
    Could you please check your validator class? Is it typecasting the request properly?

  5. Alessandro Mancini says:

    Hi, George. This is my Validator Class. It is the same that you wrote.

    package com.globinch.service;

    import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
    /**
    * The plain text password validation class.
    * @author Binu George
    * @since 2013
    * @version 1.0
    */
    public class MyServicePasswordValidator implements
    PasswordValidationCallback.PasswordValidator {

    public boolean validate(PasswordValidationCallback.Request request)
    throws PasswordValidationCallback.PasswordValidationException {

    System.err.println(“ccccc”);

    PasswordValidationCallback.PlainTextPasswordRequest plainTextRequest
    = (PasswordValidationCallback.PlainTextPasswordRequest) request;
    if (“myuserid”.equals(plainTextRequest.getUsername())
    && “mypassword”.equals(plainTextRequest.getPassword())) {
    return true;

    }else{
    //return false;
    throw new PasswordValidationCallback.PasswordValidationException(“Invalid credentials provided. Authentication failed”);

    }

    }
    }

    Did you try your code with a wrong username/password?
    My app fail only when i put wrong credentials.

    Thanks in advance.

    Alessandro.

  6. MJoe says:

    Hi,
    Can you share this application as downloadable file?
    I followed all the steps still my tomcat does not load WSIT configuration file while deploying web service.

    Thanks,

  7. Binu George says:

    Hey Joe,
    I am planning to add downloadable source code archives for each of the examples. I will update you as soon as it becomes available.
    Thanks
    Binu George

  8. Duir says:

    Hi George,

    Thank you for the article. It’s been really helpful for me.

    One question: From within my application (where I use JAX-WS RI) I log my messages through the org.apache.log4j.Logger. As you mentioned above, the PolicyConfigParser outputs certain messages about loading the WSIT configuration. My problem is that I can’t find a way to get rid of these logs or change the logging level to WARNING or ERROR. Creating a file named logging.properties on the classpath with the following line:
    com.sun.xml.ws.policy.parser.PolicyConfigParser.level=WARNING
    …as suggested here (https://www.java.net//node/676795), didn’t have any effect.
    I also tried to set the logging level at runtime with statements like:

    com.sun.istack.logging.Logger.getLogger(PolicyConfigParser.class).setLevel(Level.WARNING);
    com.sun.istack.logging.Logger.getLogger(PolicyConfigParser.class).setLevel(Level.WARNING);
    java.util.logging.Logger.getLogger(PolicyConfigParser.class.getName()).setLevel(Level.WARNING);

    But none of that seems to work. Do you have some idea as to what I’m missing here?

    Thanx, Duir

  9. Sampath says:

    Binu George,

    would you please provide me the source code.

  10. siva says:

    Could you please share the application code for download

  11. shree says:

    This is the best example i saw after my finding of two weeks or more… I know understanding a ws-security topic is such a pain and i was not able to find how to implement it using jaxws, all examples I searched earlier are so confusing for me..
    I really appriciate your work. thanks for sharing it.

  12. Surobinda Mallick says:

    Hi Binu,

    Very nice article. Few things I noticed in your exacmple come would like to inform you that :
    1. open and close quotaion is missing where you defined name space for xmlns:wsam=http://www.w3.org/2007/05/addressing/metadata.
    Hence XML validator is complaining .

    2. element definition in the xml file should be prefixed with wsp like

  13. here says:

    Excellent blog you have here.. It’s hard to find good quality writing like yours these days.
    I seriously appreciate people like you! Take care!!

  14. Binu George says:

    Thanks for the comments Surobinda

Leave a Reply

Your email address will not be published. Required fields are marked *

Paged comment generated by AJAX Comment Page

Google+ Plus Follow on Twitter Like On Facebook Linked Follow

Popular post

JAX-WS Web Services and Clients Java JAX-WS Tutorial: Develop Web Services and Clients (Consumers) Using JAX-WS
jax-ws usernametoken example JAX-WS and Secure Java Web Services using UsernameToken : WS-Security with Metro and WSIT
How to Fix : java.security.cert.CertificateException: No name matching localhost found
JAX-WS exception fault handling example JAX-WS Exceptions and Faults: Annotation, Exception and Fault Handling Examples
SOAP Binding: Difference between Document and RPC Style Web Services

Subscribe to Enterprise Java newsletter
Subscribe
Get Enterprise Java Newsletter

Enter your email and stay on top of things,