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