Master java skills

SOAP webservice basic authentication

In this tutorial we will secure a soap service with basic authentication using username/password

Step 1 : Create soap web service

Create a simple maven project

In the next screen, select packaging as war

Once the project is created, right click on the project, select Java EE Tools -> Generate Deployment Descriptor Stub. It will create web.xml under webapp/WEB-INF directory

Now, add the below dependencies in pom.xml

<dependencies>
              <dependency>
			<groupId>javax.xml.ws</groupId>
			<artifactId>jaxws-api</artifactId>
			<version>2.2.6</version>
	      </dependency>          
              <dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
			<version>2.3.1</version>
	       </dependency>      
               <dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-frontend-jaxws</artifactId>
			<version>3.1.11</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-transports-http</artifactId>
			<version>3.1.11</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>
</dependencies>

Also add following build plugin

	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.6.1</version>
					<configuration>
						<source>1.8</source>
						<target>1.8</target>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-war-plugin</artifactId>
					<version>3.0.0</version>
					<configuration>
						<warSourceDirectory>src/main/webapp</warSourceDirectory>
						<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
						<warName>JaxWs</warName>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>

Complete pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sks</groupId>
	<artifactId>hello-service-with-security</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<dependencies>
	        <dependency>
			<groupId>javax.xml.ws</groupId>
			<artifactId>jaxws-api</artifactId>
			<version>2.2.6</version>
		</dependency>
	        <dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
			<version>2.3.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-frontend-jaxws</artifactId>
			<version>3.1.11</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-transports-http</artifactId>
			<version>3.1.11</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>4.3.8.RELEASE</version>
		</dependency>
	</dependencies>

	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.6.1</version>
					<configuration>
						<source>1.8</source>
						<target>1.8</target>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-war-plugin</artifactId>
					<version>3.0.0</version>
					<configuration>
						<warSourceDirectory>src/main/webapp</warSourceDirectory>
						<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
						<warName>JaxWs</warName>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>

</project>

Write a java class in src/main/java folder

package com.sks;

import javax.jws.WebMethod;
import javax.jws.WebService;
 
@WebService
public class HelloWorld {
 
    @WebMethod
    public String sayHello(String name) {
        return "hello " + name;
    }
}

Under the webapp/WEB-INF folder create a file named cxf-servlet.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:cxf="http://cxf.apache.org/core"
 xmlns:soap="http://cxf.apache.org/bindings/soap"
 xsi:schemaLocation="http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
 
 <jaxws:server id="helloworld" address="/hello">
 <jaxws:serviceBean>
 <bean class="com.sks.HelloWorld" />
 </jaxws:serviceBean>
  
 </jaxws:server>
 
 
</beans>

Configure cxf servlet in web.xml like below

<?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"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	<display-name>JavaWs</display-name>
	<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>
</web-app>

Run As -> Maven install and deploy it on the tomcat server

Once the server is started successfully, access the wsdl on the browser using the below url

http://localhost:8080/JaxWs/services/hello?wsdl

Step 2 : Write the client

Create a simple maven project with pom.xml like below

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sks</groupId>
	<artifactId>hello-service-client</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>jaxws-maven-plugin</artifactId>
				<version>2.5</version>
				<executions>
					<execution>
						<id>wsimport-from-jdk</id>
						<goals>
							<goal>wsimport</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<wsdlUrls>
						<wsdlUrl>
							http://localhost:8080/JaxWs/services/hello?wsdl
						</wsdlUrl>
					</wsdlUrls>
					<keep>true</keep>
					<packageName>com.sks.generated</packageName>
					<sourceDestDir>src/main/java</sourceDestDir>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

Run As -> Maven install

It will generate java classes under the directory com.sks.generated like below

Write the main class HelloServiceClientTest.java


package com.sks;

import com.sks.generated.HelloWorld;
import com.sks.generated.HelloWorldService;

public class HelloServiceClientTest {

	public static void main(String[] args) {

		HelloWorldService service = new HelloWorldService();

		HelloWorld helloWorld = service.getHelloWorldPort();

		System.out.println(helloWorld.sayHello("Shalini"));

	}

}

Run the class. Output should be like below

Step 3 : Adding the security to the web service

In the soap web service project hello-service-with-security add the below dependency

<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-ws-security</artifactId>
	<version>3.1.11</version>
</dependency>

Write a class UserPasswordCallbackHandler.java under com.sks directory.

Note -> username and password is kept ‘plain’ in the below class

package com.sks;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

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

import org.apache.wss4j.common.ext.WSPasswordCallback;

public class UserPasswordCallbackHandler implements CallbackHandler {

	private Map<String, String> userPasswordMap = new HashMap<>();

	public UserPasswordCallbackHandler() {
		userPasswordMap.put("plain", "plain");
	}

	@Override
	public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
		for (Callback callback : callbacks) {
			WSPasswordCallback pc = (WSPasswordCallback) callback;

			String pass = userPasswordMap.get(pc.getIdentifier());
			if (pass != null) {
				pc.setPassword(pass);
				return;
			}
		}
	}
}

Add the following in cxf-servlet.xml

<jaxws:inInterceptors>
	<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
		<constructor-arg>
			<map>
				<entry key="action" value="UsernameToken" />
				<entry key="passwordType" value="PasswordText" />
				<entry key="passwordCallbackRef"
					value-ref="myPasswordCallback" />
			</map>
		</constructor-arg>
	</bean>
</jaxws:inInterceptors>

Add bean definition also in cxf-servlet.xml

<bean id="myPasswordCallback" class="com.sks.UserPasswordCallbackHandler" />

Refer below screenshot

Now, if you again the run client main class, you get exception because the web service has been secured now

Step 4 : Update the client

Write a new class UserPasswordCallbackHandler under com.sks package

package com.sks;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

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

import org.apache.wss4j.common.ext.WSPasswordCallback;

public class UserPasswordCallbackHandler implements CallbackHandler {

	private Map<String, String> userPasswordMap = new HashMap<>();

	public UserPasswordCallbackHandler() {
		userPasswordMap.put("plain", "plain");
	}

	@Override
	public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
		for (Callback callback : callbacks) {
			WSPasswordCallback pc = (WSPasswordCallback) callback;

			String pass = userPasswordMap.get(pc.getIdentifier());
			if (pass != null) {
				pc.setPassword(pass);
				return;
			}
		}
	}
}

Update the main class HelloServiceClientTest


package com.sks;

import java.util.HashMap;
import java.util.Map;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.handler.WSHandlerConstants;

import com.sks.generated.HelloWorld;
import com.sks.generated.HelloWorldService;

public class HelloServiceClientTest {

	public static void main(String[] args) {

		HelloWorldService service = new HelloWorldService();

		HelloWorld helloWorld = service.getHelloWorldPort();

		Client client = ClientProxy.getClient(helloWorld);
		Endpoint endpoint = client.getEndpoint();

		Map<String, Object> props = new HashMap<>();
		props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
		props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
		props.put(WSHandlerConstants.PW_CALLBACK_CLASS, UserPasswordCallbackHandler.class.getName());
		props.put(WSHandlerConstants.USER, "plain");

		WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(props);
		endpoint.getOutInterceptors().add(wssOut);
		System.out.println(helloWorld.sayHello("Shalini"));
	}
}

Run the client code by running the main class