Wednesday, July 27, 2016

Enabling WS-Security in Spring Boot using CXF, JAX-WS and JAXB

In my previous post, I've shown how to quickly create a WSDL/SOAP based web service. This post will build on top of that to include WS-Security. We'll be using simple username/password authentication.



Add the following artifact into your Spring Boot pom.xml:


    org.apache.cxf
    cxf-rt-ws-security
    3.1.6


We create a callback handler class to perform our custom authentication. This class implements the CallbackHandler interface:

package com.techtots.security;

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.wss4j.common.ext.WSPasswordCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WSSecurityCallback implements CallbackHandler {

    
    private static final Logger log = LoggerFactory.getLogger(WSSecurityCallback.class);

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

        WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];

        log.info("Identifier: " + callback.getIdentifier());

        // you won't be able to retrieve the password using callback.getPassword().
        // to authenticate a user, you'll need to set the password tied to the user.
        // user credentials are typically retrieved from DB or your own authentication source.
        // if the password set here is the same as the password passed by caller, authentication is successful.
        callback.setPassword("wspassword");
                    
    }
}

In order to enable WS-Security, we'll need to change the WebServiceConfig class as below:

@Bean
public EndpointImpl userServiceEndpoint() {
    EndpointImpl ep = new EndpointImpl(springBus(), new UserServiceImpl());
    ep.publish("/UserService");
    
    Endpoint cxfEndPoint = ep.getServer().getEndpoint();
    
    Map inProps = new HashMap<>();
    inProps.put(ConfigurationConstants.ACTION, ConfigurationConstants.USERNAME_TOKEN);
    inProps.put(ConfigurationConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
    inProps.put(ConfigurationConstants.PW_CALLBACK_CLASS, WSSecurityCallback.class.getName());
    
    WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps);
    cxfEndPoint.getInInterceptors().add(wssIn);
    
    return ep;
}

Full WebServiceConfig class as below:

package com.techtots;

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

import javax.security.auth.callback.CallbackHandler;

import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.wss4j.common.ConfigurationConstants;
import org.apache.wss4j.dom.WSConstants;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.techtots.security.WSSecurityCallback;
import com.techtots.services.UserServiceImpl;

@Configuration
public class WebServiceConfig {
    @Bean
    public ServletRegistrationBean dispatcherSerlvet() {
        return new ServletRegistrationBean(new CXFServlet(), "/services/*");
    }
    
    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        return new SpringBus();
    }
    
    @Bean
    public CallbackHandler usernameTokenCallback() {
        return new WSSecurityCallback();
    }
    
    @Bean
    public EndpointImpl userServiceEndpoint() {
        EndpointImpl ep = new EndpointImpl(springBus(), new UserServiceImpl());
        ep.publish("/UserService");
        
        Endpoint cxfEndPoint = ep.getServer().getEndpoint();
        
        Map inProps = new HashMap<>();
        inProps.put(ConfigurationConstants.ACTION, ConfigurationConstants.USERNAME_TOKEN);
        inProps.put(ConfigurationConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
        inProps.put(ConfigurationConstants.PW_CALLBACK_CLASS, WSSecurityCallback.class.getName());
        
        WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps);
        cxfEndPoint.getInInterceptors().add(wssIn);
        
        return ep;
        
    }

}

Startup your app and you should see your normal WSDL. But sending a "normal" request will result in the following response:


   
      
         ns1:SecurityError
         A security error was encountered when verifying the message
      
   


You'll need to add the WSSE headers to include username and password:



    
        
            
                wsuser
                wspassword
                v+pC7eS4q1tG+GGolKBrgw==
                2016-07-27T02:48:46.895Z
            
        
    
    
        
            
                techtots
                234234324
                techtots@gmail.com
            
        
    


You can set this via SoapUI under Request Properties as shown in the screenshot below:


7 comments:

Padhma said...

very interesting security purpose article,but i want more details about it.
Back to original

Unknown said...
This comment has been removed by the author.
Ancy merina said...
This comment has been removed by the author.
Chris Topinka said...

What are the steps to put the tls headers on every request?

Chris Topinka said...

I'm trying to make requests to service and believe I need this configured as an out interceptor?

https://gist.github.com/christopinka/d4303c73431aca8d99c6fc153fa06e10

Pallavi karthi said...

Thank you very much for this article. I think that i can consider this article as a reference for me because it contains many important information at once and shortcut too much time, instead of reading more articles.
Paper Publishing Sites
Naas Rated Journals
Language Translation Services
Research Paper Writing Service
Article Writing Services

Laila Thakur said...

Excellent Article!!! I like the helpful information you provide in your article.uniraj bsc 1st year result 2021 subject wise