Apache CXF JAX-WS: Web Service using Top-Down approach + Adding WS-Security policy using UsernameToken profile

In this article, we will add UsernameToken security headers to the demo example seen in the last article “Web Service using top-down approach

There are two options available to protect the exposed web service

  • Adding WS-Security policy to WSDL document
  • Implementing CXF interceptors (policy-free WSDL)

We will illustrate an example for WS-Security policy here and in the next article we will explore how can we erase policy chunk from WSDL document & achieve the same effect using Apache CXF interceptor

This UsernameToken profile works even without transport-level encryption i.e.; Secured Socket Layer (SSL) in the server but prefer enabling SSL which encrypts the username/password transacted between the client & server

Below article explains how we can activate/enable SSL in Apache Tomcat server
Apache Tomcat –> activating transport layer security see here

Technology Used

  • Java 1.7
  • Eclipse Luna IDE
  • Apache CXF-3.0.2
  • Spring-4.1.0.RELEASE
  • Apache Maven 3.2.1
  • Apache Tomcat-8.0.15
  • Oracle Weblogic server 12c

Mavenize or download required jars

Add apache-cxf-3.0.2 and its security & spring-4.1.0.RELEASE dependencies to pom.xml

<!-- properties -->
<properties>
	<cxf.version>3.0.2</cxf.version>
	<spring.version>4.1.0.RELEASE</spring.version>
	<cxf.scope>compile</cxf.scope>
	<compileSource>1.7</compileSource>
	<maven.compiler.target>1.7</maven.compiler.target>
	<maven.compiler.source>1.7</maven.compiler.source>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
	<!-- apache cxf jax-ws 3.0.2 -->
	<dependency>
		<groupId>org.apache.cxf</groupId>
		<artifactId>cxf-rt-frontend-jaxws</artifactId>
		<version>${cxf.version}</version>
		<scope>${cxf.scope}</scope>
	</dependency>
	<dependency>
		<groupId>org.apache.cxf</groupId>
		<artifactId>cxf-rt-transports-http</artifactId>
		<version>${cxf.version}</version>
		<scope>${cxf.scope}</scope>
	</dependency>
	<dependency>
		<groupId>org.apache.cxf</groupId>
		<artifactId>cxf-rt-ws-security</artifactId>
		<version>${cxf.version}</version>
		<scope>${cxf.scope}</scope>
	</dependency>

	<!-- spring framework 4.1.0 -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-web</artifactId>
		<version>${spring.version}</version>
		<scope>compile</scope>
	</dependency>
</dependencies>

Folks who aren’t familiar with Maven concepts or don’t require maven for their project, can download the below jars individually from the central repository or maven repository or maven2 and include them in the classpath

Steps to generate Java artifacts from WSDL/XSD

  • write/design XML Schema (XSD)
  • similarly, write/design WSDL document including above XSD for Type attributes
  • configure maven plugins (wsimport/wsdl2java goal) in pom.xml with correct and complete path of the wsdl file under wsdlOptions/wsdlOption
  • Run maven command “mvn generate-sources” from project’s context-root
  • java artifacts will be generated under “generated” folder within specified targetNamespace

1_Metro-JAX-WS-Top-Down_web_service_resources

Let us understand above steps in more detail

Write/design well-formed XML Schema

Book.xsd (src/main/resources/com/jaxws/series/top/down/approach/entities)

Below XSD contains two elements with name “BookRequestType” and “BookResponseType

  • BookRequestType contains single string called isbnNumber
  • BookResponseType contains four sub-elements namely bookISBN, bookName, author and category
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	targetNamespace="http://benchresources.in/entities/Book" xmlns:tns="http://benchresources.in/entities/Book"
	elementFormDefault="qualified">

	<!-- Book Request Type -->
	<xsd:element name="BookRequestType">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element name="isbnNumber" type="xsd:string" />
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>

	<!-- Book Response Type -->
	<xsd:element name="BookResponseType">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element name="bookISBN" type="xsd:string" />
				<xsd:element name="bookName" type="xsd:string" />
				<xsd:element name="author" type="xsd:string" />
				<xsd:element name="category" type="xsd:string" />
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>

</xsd:schema>

Write/design well-formed WSDL

BookService.wsdl (src/main/resources/com/jaxws/series/top/down/approach/services)

This WSDL document describes,

  • This is the contract document for Book Service exposing one operation called “getBookByISDNRequestNumber” whose input argument is “BookRequestType” and return type is “BookResponseType
  • Policy element is added at the end of this WSDL file and this policy is referenced from <wsdl:binding> section (from <wsp:PolicyReference> element)
  • UsernameToken element (<sp:UsernameToken10>) configured to asserts/validate username/password and this ensures to verify every SOAP requests
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	targetNamespace="http://benchresources.in/services/BookService/"
	xmlns:tns="http://benchresources.in/services/BookService/" xmlns:book="http://benchresources.in/entities/Book"
	xmlns:wsp="http://www.w3.org/ns/ws-policy"
	xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
	xmlns:fi="http://java.sun.com/xml/ns/wsit/2006/09/policy/fastinfoset/service"
	xmlns:tcp="http://java.sun.com/xml/ns/wsit/2006/09/policy/soaptcp/service"
	xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
	xmlns:sc="http://schemas.sun.com/2006/03/wss/server" xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy"
	name="BookService">

	<wsdl:types>
		<xsd:schema targetNamespace="http://benchresources.in/services/BookService/">
			<xsd:import namespace="http://benchresources.in/entities/Book"
				schemaLocation="../entities/book.xsd" />
		</xsd:schema>
	</wsdl:types>

	<wsdl:message name="BookRequest">
		<wsdl:part element="book:BookRequestType" name="parameters" />
	</wsdl:message>
	<wsdl:message name="BookResponse">
		<wsdl:part element="book:BookResponseType" name="parameters" />
	</wsdl:message>

	<wsdl:portType name="IBookService">
		<wsdl:operation name="getBookByISDNRequestNumber">
			<wsdl:input message="tns:BookRequest" />
			<wsdl:output message="tns:BookResponse" />
		</wsdl:operation>
	</wsdl:portType>

	<wsdl:binding name="BookServiceSOAPBinding" type="tns:IBookService">
		<wsp:PolicyReference URI="#BookServiceSOAPBindingPolicy" />
		<soap:binding style="document"
			transport="http://schemas.xmlsoap.org/soap/http" />
		<wsdl:operation name="getBookByISDNRequestNumber">
			<soap:operation soapAction="" />
			<wsdl:input>
				<soap:body use="literal" />
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal" />
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>

	<wsdl:service name="BookService">
		<wsdl:port name="BookServicePort" binding="tns:BookServiceSOAPBinding">
			<soap:address
				location="http://localhost:8080/ApacheCXF-JAX-WS-Top-Down/services/book/BookService" />
		</wsdl:port>
	</wsdl:service>

	<wsp:Policy wsu:Id="BookServiceSOAPBindingPolicy">
		<wsp:ExactlyOne>
			<wsp:All>
				<sp:SupportingTokens>
					<wsp:Policy>
						<sp:UsernameToken
							sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
							<wsp:Policy>
								<sp:WssUsernameToken10 />
							</wsp:Policy>
						</sp:UsernameToken>
					</wsp:Policy>
				</sp:SupportingTokens>
			</wsp:All>
		</wsp:ExactlyOne>
	</wsp:Policy>

</wsdl:definitions>

Configure maven plugin in pom.xml (wsdl2java goal)

This plugin which defines wsdl2java goal from cxf-codegen-plugin generates java artifacts from the supplied WSDL file under resources folder

<!-- plugin 4- apache cxf codegen wsdl2java goal -->
<plugin>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-codegen-plugin</artifactId>
	<version>3.0.2</version>
	<executions>
		<execution>
			<configuration>
				<sourceRoot>${basedir}/generated/java/source</sourceRoot>
				<wsdlOptions>
					<wsdlOption>
						<wsdl>${basedir}/src/main/resources/com/jaxws/series/top/down/approach/services/BookService.wsdl
						</wsdl>
					</wsdlOption>
				</wsdlOptions>
			</configuration>
			<goals>
				<goal>wsdl2java</goal>
			</goals>
		</execution>
	</executions>
</plugin>

Run “mvn generate-sources”

Look at the generated java source files in the generated folder

After running above maven command, you will get to see below generated java files

2_Metro-JAX-WS-Top-Down_generated_java_artifacts

  • IBookService.java
  • BookRequestType.java
  • BookResponseType.java
  • BookService.java
  • ObjectFactory.java
  • package-info.java

We will look at one file IBookService.java, for other files you can download this eclipse project provided in the last section “Download Project”

This interface which is implemented by our endpoint business implementation class

IBookService.java

package in.benchresources.services.bookservice;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlSeeAlso;

/**
 * This class was generated by Apache CXF 3.0.2
 * 2014-11-20T00:05:57.431+05:30
 * Generated source version: 3.0.2
 *
 */
@WebService(targetNamespace = "http://benchresources.in/services/BookService/", name = "IBookService")
@XmlSeeAlso({in.benchresources.entities.book.ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface IBookService {

    @WebMethod
    @WebResult(name = "BookResponseType", targetNamespace = "http://benchresources.in/entities/Book", partName = "parameters")
    public in.benchresources.entities.book.BookResponseType getBookByISDNRequestNumber(
        @WebParam(partName = "parameters", name = "BookRequestType", targetNamespace = "http://benchresources.in/entities/Book")
        in.benchresources.entities.book.BookRequestType parameters
    );
}

 

Directory Structure

Before moving on, let us understand the directory/package structure once you create project and/plus after generating java artifacts in Eclipse IDE

Maven has to follow certain directory structure

  • src/test/java –> test related files, mostly JUnit test cases
  • src/main/java –> create java source files under this folder
  • generated/java/source –> generated java source files are placed here
  • src/main/resources –> all configuration files placed here
  • src/test/resources –> all test related configuration files placed here
  • Maven Dependencies or Referenced Libraries –> includes jars in the classpath
  • WEB-INF under webapp –> stores web.xml & other configuration files related to web application

Project Structure (Package Explorer view in Eclipse)

3_ApacheCXF-JAX-WS-Top-Down-Security-Policy_Project_Structure_In_Eclipse

Jar Libraries Used in the Project (Maven Dependencies)

4_ApacheCXF-JAX-WS-Top-Down-Security-Policy_Jars_In_Classpath

Web application

For any web application, entry point is web.xml which describes how the incoming http requests are served / processed. Further, it describes about the global-context and local-context param (i.e.; <context-param> & <init-param>) for loading files particular to project requirements & contains respective listener

With this introduction, we will understand how we configured web.xml for Apache CXF JAX-WS SOAP based Web Service

web.xml (the entry point –> under WEB-INF)

This web.xml file describes,

  • Like any JEE web framework register “org.apache.cxf.transport.servlet.CXFServlet” with servlet container
  • http requests with URL pattern “/services/*” will be sent to the registered servlet called “CXFServlet” i.e.; (org.apache.cxf.transport.servlet.CXFServlet)
  • configure spring context loader listener for loading spring context files “org.springframework.web.context.ContextLoaderListener
  • <context-param> with its attributes describes the location of the “apache-cxf-service.xml” file from where it has to be loaded. We will discuss briefly about this file
  • configure session timeout in secs using <session-config> tag
  • <welcome-file-list> files under this tag is the start-up page

web.xml

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

	<display-name>ApacheCXF-JAX-WS-Top-Down-Security-Policy</display-name>

	<!-- Apache CXF servlet -->
	<servlet>
		<servlet-name>CXFServlet</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>CXFServlet</servlet-name>
		<url-pattern>/services/*</url-pattern>
	</servlet-mapping>

	<!-- listener -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- web context param -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>WEB-INF/apache-cxf-services.xml</param-value>
	</context-param>

	<!-- session timeout -->
	<session-config>
		<session-timeout>60</session-timeout>
	</session-config>

	<!-- welcome file list -->
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>

</web-app>

 

Apache CXF services

Apache CXF comes with spring based configuration, so it is easy to register beans in the spring container much like we do any bean in spring application in addition to configuring JAX-WS endpoints and interceptors, etc

In CXF endpoint, we can define implementor i.e.; Java endpoint implementation class and address. So, incoming requests from “CXFServlet” servlet invokes corresponding implementation class with configured address-pattern

For more JAX-WS element details see here

This apache-cxf-services.xml describes,

  • < jaxws:endpoint /> defines which service implementation class to be invoked for the incoming http/https SOAP requests with address-pattern configured
  • configure WS-Security policy under < jaxws:endpoint />, so the incoming http/https requests will be intercepted and their credentials are verified for authentication
  • configure a bean for server-side password callback and this will be referenced from above configured WS-Security policy
  • this server-side password callback reference class provides a correct password for the invoking client, which is compared with the value sent in the client request

NOTE: For two different beans we can have two different url-pattern (address) like

<jaxws:endpoint id="bookservice"
	implementor="com.jaxws.series.top.down.approach.service.BookServiceImpl"
	address="/book">
</jaxws:endpoint>

<jaxws:endpoint id="otherservice" 
        implementor="other.qualified.package.name"
	address="/other">
</jaxws:endpoint>

 

apache-cxf-services.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
	xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

	<jaxws:endpoint id="bookservice"
		implementor="com.jaxws.series.top.down.approach.service.BookServiceImpl"
		address="/book" wsdlLocation="com/jaxws/series/top/down/approach/services/BookService.wsdl">

		<jaxws:features>
			<bean class="org.apache.cxf.feature.LoggingFeature" />
		</jaxws:features>

		<jaxws:properties>
			<entry key="ws-security.callback-handler" value-ref="passwordCallback" />
		</jaxws:properties>

	</jaxws:endpoint>

	<bean id="passwordCallback"
		class="com.jaxws.series.top.down.approach.service.utils.AuthenticationCallbackHandler" />

</beans>

 

Let’s see coding in action

 

URL Pattern

Http url for any common web application is http://<server>:<port>/<root-context>/<from_here_application_specific_path>

In our example, we are going to deploy the war into Tomcat 8.0 server, so our server and port are localhost and 8080 respectively. Context root is the project name i.e.; ApacheCXF-JAX-WS-Top-Down-Security-Policy. Initial path for this application is http://localhost:8080/ApacheCXF-JAX-WS-Top-Down-Security-Policy

We have configured “/services/*” as url-pattern for the “CXFServlet” servlet in web.xml and our business implementation class implements the portType interface generated from WSDL file which is annotated with @WebService annotation at class-level

 

Service side validation handler

This server side class asserts/validates the incoming SOAP headers which contains username/password and subsequently service the request on successful authenticated case, otherwise throwing error

This class compares the password SET in the method with the password of the invoking client request

NOTE: This example depicts a very simple case where it compares string values. For more complex application, there could be a separate LDAP server or database which stores username/password

AuthenticationCallbackHandler.java

package com.jaxws.series.top.down.approach.service.utils;

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.cxf.interceptor.security.AuthenticationException;
import org.apache.wss4j.common.ext.WSPasswordCallback;

public class AuthenticationCallbackHandler  implements CallbackHandler {

	WSPasswordCallback wsPasswordCallback = null;

	@Override
	public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException, AuthenticationException  { 

		wsPasswordCallback = (WSPasswordCallback) callbacks[0];

		if(wsPasswordCallback.getIdentifier().equals("sj")) {

			// set the password on the callback. This will be compared to the password which was sent from the client.
			wsPasswordCallback.setPassword("benchresources");
		}
	}
}

 

Book Service Implementation (business logic)

This service provider class implements portType interface generated from WSDL file. Also, class got annotated with @WebService annotation at class-level and this is very important

Always, keep a practice of defining most of the attributes of @WebService annotation if not all while developing top-down approach. Defining means providing values of the following attributes

  • serviceName (this is very important, as it defines/controls endpoint URL)
  • endpointInterface (this is mandatory)
  • targetNamespace (same as in the WSDL file)
  • portName (port value under <service> element)
  • name (optional)
  • wsdlLocation (optional)

BookServiceImpl.java

package com.jaxws.series.top.down.approach.service;

import in.benchresources.entities.book.BookRequestType;
import in.benchresources.entities.book.BookResponseType;
import in.benchresources.services.bookservice.IBookService;

import javax.jws.WebService;

import org.springframework.stereotype.Service;

@WebService(serviceName="BookService", endpointInterface="in.benchresources.services.bookservice.IBookService",
targetNamespace="http://benchresources.in/services/BookService/", portName="BookServicePort", name="BookServiceImpl")
public class BookServiceImpl implements IBookService {

	// http://localhost:8080/ApacheCXF-JAX-WS-Top-Down-Security-Policy/services/book/BookService?wsdl

	public BookResponseType getBookByISDNRequestNumber(BookRequestType parameters) {

		// create object of responseType and set values & return
		BookResponseType bookResponseType = new BookResponseType();
		bookResponseType.setBookISBN(parameters.getIsbnNumber());
		bookResponseType.setBookName("Objective Microbiology");
		bookResponseType.setAuthor("S. Nandi");
		bookResponseType.setCategory("Microbiology");
		return bookResponseType;
	}
}

 

That’s all with coding part, now let us move on to deployment and testing

 

Apache Tomcat-8.0.15 Deployment

  • Run maven command to build the war: mvn clean install (use command prompt or integrated maven in eclipse IDE
  • Copy(ctrl+c) the war file from the target folder
  • Paste(ctrl+v) it into apache tomcat (webapps folder)
  • Start the tomcat server (Tomcat_Home\bin\startup.bat)

Oracle Weblogic server 12.1.1.0 Deployment

Apache CXF based JAX-WS web service can’t be deployed directly into Oracle WebLogic server as WAR file –> it’s a two step process. First build a WAR file and then package this WAR file into an EAR file –> Deploy EAR file into weblogic server

See this article for explanation: Packaging WAR as EAR

Steps to be followed

  • Run maven command to build the war: mvn clean install (use command prompt or integrated maven in eclipse IDE)
  • Once you see “BUILD SUCCESS” after running maven command, it means your war file is successfully built and installed in the local maven repository
  • Package this WAR file into an EAR file as explained in this article
  • Start weblogic 12c application server and hit the URL http://localhost:7001/console in any of the latest web browser and enter username/password you configured while setting up weblogic 12c server
  • Go to Deployments –> click install button –> browse through EAR file location –> say Next –> say Next –> Finish

For Oracle WebLogic 12c server Installation steps see here

Test the service !!

Testing

There are many ways to do testing

  • SOAP UI Client
  • Java Client using JDK’s in-built classes like HttpURLConnection
  • Java Client using SOAP API
  • Eclipse’s Web Services Explorer
  • Write your own client for example, Java client using httpcomponents from Apache

We will cover first 2 ways of testing above JAX-WS deployed service

1. SOAP UI Client

Load the endpoint URL in SOAP UI Client, which will pre-populate the request XML based on the operation deployed/exposed using WSDL

For example, http://localhost:8080/ApacheCXF-JAX-WS-Top-Down-Security-Policy/services/book/BookService?wsdl

Note: This pre-populated request XML doesn’t contain header tag with the required elements like Username & Password and also namespace used. Manually add these tags in the SOAP request XML

Request XML

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
	xmlns:book="http://benchresources.in/entities/Book">
	<soapenv:Header>
		<wsse:Security
			xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
			xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
			soapenv:mustUnderstand="1">
			<wsse:UsernameToken wsu:Id="UsernameToken-1">
				<wsse:Username>sj</wsse:Username>
				<wsse:Password
					Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">benchresources</wsse:Password>
			</wsse:UsernameToken>
		</wsse:Security>
	</soapenv:Header>
	<soapenv:Body>
		<book:BookRequestType>
			<book:isbnNumber>ISBN-2134</book:isbnNumber>
		</book:BookRequestType>
	</soapenv:Body>
</soapenv:Envelope>

Response XML

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Body>
		<BookResponseType xmlns="http://benchresources.in/entities/Book">
			<bookISBN>ISBN-2134</bookISBN>
			<bookName>Objective Microbiology</bookName>
			<author>S. Nandi</author>
			<category>Microbiology</category>
		</BookResponseType>
	</soap:Body>
</soap:Envelope>

5_ApacheCXF-JAX-WS-Top-Down-Security-Policy_SOAP_UI_Testing

 

 

2. Java client

For this java client to work/execute, we don’t need to add any extra jars or any new dependency in pom.xml as these classes comes shipped along with JDK. Observe, import statements closely for this client

Note: Request XML pattern formed taking help from pre-populated request XML from SOAP UI client as explained above

TestBookService.java

package test.jaxws.series.top.down.approach.service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;

public class TestBookService {

	/**
	 * Apache CXF - JAX-WS top-down web service approach
	 * main() method to test/start soap web service
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {

		String httpRequestURL = "http://localhost:8080/ApacheCXF-JAX-WS-Top-Down-Security-Policy/services/book/BookService?wsdl";
		String soapRequestParam = 	"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:book=\"http://benchresources.in/entities/Book\">"
				+						"<soapenv:Header>"
				+							"<wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" soapenv:mustUnderstand=\"1\">"
				+								"<wsse:UsernameToken wsu:Id=\"UsernameToken-1\">"
				+									"<wsse:Username>sj</wsse:Username>"
				+									"<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">benchresources</wsse:Password>"
				+								"</wsse:UsernameToken>"
				+							"</wsse:Security>"
				+						"</soapenv:Header>"
				+						"<soapenv:Body>"
				+							"<book:BookRequestType>"
				+								"<book:isbnNumber>ISBN-3245</book:isbnNumber>"
				+							"</book:BookRequestType>"
				+						"</soapenv:Body>"
				+					"</soapenv:Envelope>";
		String responseString = testBookService(httpRequestURL, soapRequestParam);
		System.out.println("Response String : " + responseString);
	}

	/**
	 * This method uses HttpURLConnection to invoke exposed SOAP web service and returns the response string to the calling client
	 *
	 * @param httpRequestURL
	 * @param requestXmlParam
	 * @return responseXML
	 * @throws IOException
	 */
	public static String testBookService(String httpRequestURL, String requestXmlParam) throws IOException {

		// local variables
		URL url = null;
		HttpURLConnection httpURLConnection = null;
		OutputStreamWriter outputStreamWriter = null;
		String responseMessageFromServer = null;
		String responseXML = null; 

		try {
			// set basic request parameters
			url = new URL(httpRequestURL);
			httpURLConnection = (HttpURLConnection) url.openConnection();
			httpURLConnection.setDoOutput(true);
			httpURLConnection.setRequestMethod("POST");
			//			httpURLConnection.setRequestProperty("SOAPAction", "");
			httpURLConnection.setRequestProperty("Content-Type", "text/xml");
			httpURLConnection.setRequestProperty("Accept", "text/xml");

			// write request XML to the HTTP request
			outputStreamWriter = new OutputStreamWriter(httpURLConnection.getOutputStream());
			outputStreamWriter.write(requestXmlParam);
			outputStreamWriter.flush();

			System.out.println("Response code: " + httpURLConnection.getResponseCode());
			if (httpURLConnection.getResponseCode() == 200) {

				responseMessageFromServer = httpURLConnection.getResponseMessage();
				System.out.println("ResponseMessageFromServer: " + responseMessageFromServer);
				responseXML = getResponseXML(httpURLConnection);
			}
		}
		catch (IOException ioex) {
			ioex.printStackTrace();
			throw ioex;
		}
		finally{
			// finally close all operations
			outputStreamWriter.close();
			httpURLConnection.disconnect();
		}
		return responseXML;
	}

	/**
	 * This method is used to get response XML from the HTTP POST request
	 *
	 * @param httpURLConnection
	 * @return stringBuffer.toString()
	 * @throws IOException
	 */
	private static String getResponseXML(HttpURLConnection httpURLConnection) throws IOException {

		// local variables
		StringBuffer stringBuffer = new StringBuffer();
		BufferedReader bufferedReader = null;
		InputStreamReader inputStreamReader = null;
		String readSingleLine = null;

		try{
			// read the response stream AND buffer the result into a StringBuffer
			inputStreamReader = new InputStreamReader(httpURLConnection.getInputStream());
			bufferedReader = new BufferedReader(inputStreamReader);

			// reading the XML response content line BY line
			while ((readSingleLine = bufferedReader.readLine()) != null) {
				stringBuffer.append(readSingleLine);
			}
		}
		catch (IOException ioex) {
			ioex.printStackTrace();
			throw ioex;
		}
		finally{
			// finally close all operations
			bufferedReader.close();
			httpURLConnection.disconnect();
		}
		return stringBuffer.toString();
	}
}

Output in console

Response code: 200
ResponseMessageFromServer: OK
Response String : 
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Body>
		<BookResponseType xmlns="http://benchresources.in/entities/Book">
			<bookISBN>ISBN-3245</bookISBN>
			<bookName>Objective Microbiology</bookName>
			<author>S. Nandi</author>
			<category>Microbiology</category>
		</BookResponseType>
	</soap:Body>
</soap:Envelope>

Conclusion: With WS-Security policy using UsernameToken profile, we can protect the exposed SOAP based Web Service

This example covers policy-plus WSDL document for adding WS-Security to the exposed web service. In the next article, we will explore how can we minimize the policy from WSDL document and achieve same security effect implementing Apache CXF interceptor concept

Download project

ApacheCXF-JAX-WS-Top-Down-Security-Policy (15kB)

Happy Coding !!
Happy Learning !!

Apache CXF JAX-WS: Web Service using Top-Down approach + Adding CXF interceptors using UsernameToken
Apache CXF JAX-WS: SOAP based Web Service using Bottom-Up approach
  • basanta hota

    Any one can help me out same example i tried , even i am not pass any headers also it’s accessing from SOAP UI.
    if i will pass

    Basant
    Sri

    its giving me error how to resolve this issue ..