Search code examples
springweb-serviceswsdlcxfws-security

Enable Police in CXF 3.0 and Spring with Contract-First


I'm trying implement WS-Policy in my services with CXF 3.1.0 and Spring 4.1.6

Most of examples that I found was with CXF 2 and structures like cxf-extension-policy.xml and cxf-extension-ws-security.xml changed in CXF new version.

I tried something like:

package spring;

import java.util.LinkedList;
import java.util.List;

import javax.xml.ws.Endpoint;

import org.apache.cxf.Bus;
import org.apache.cxf.feature.AbstractFeature;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.ws.policy.WSPolicyFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

import com.student.StudentService;
import com.student.service.StudentServiceImpl;

@Configuration
@ImportResource({"classpath:META-INF/cxf/cxf.xml"})
public class CXFConfig {

@Autowired
private Bus cxfBus;

@Bean
public StudentService service(){
    return new StudentServiceImpl();
}

@Autowired
@Bean
public Endpoint serviceImpl(
        final StudentService service){

    EndpointImpl endpoint = new EndpointImpl(this.cxfBus, service);
    endpoint.setAddress("/StudentService");
    endpoint.publish();

    return endpoint;
}
}

My WSDL have some policies and is basically the WSDL from this tutorial

I generate my Java classes using Contract-first.

When I run my project my policies don't appears.

I tried

final Map<String, Object> properties = new HashMap<>();
properties.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
properties.put(WSHandlerConstants.PW_CALLBACK_REF, ServerPasswordCallback.class.getName());

EndpointImpl endpoint = new EndpointImpl(this.cxfBus, portType);
endpoint.setProperties(properties);
endpoint.setAddress("/StudentService");
endpoint.publish();

return endpoint;

I also tryed

final Map<String, Object> properties = new HashMap<>();
properties.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
properties.put(WSHandlerConstants.PW_CALLBACK_REF, ServerPasswordCallback.class.getName());

cxfBus.getFeatures().add(new WSPolicyFeature());
cxfBus.getInInterceptors().add(new WSS4JInInterceptor(properties));

EndpointImpl endpoint = new EndpointImpl(this.cxfBus, portType);
endpoint.setAddress("/StudentService");
endpoint.publish();

return endpoint;

And

WSPolicyFeature wsPolicyFeature = new WSPolicyFeature();
wsPolicyFeature.initialize(this.cxfBus);

None of this works.

Does someone knows how to configure this with Spring annotations?


Solution

  • After a while, I discover that WSS4J uses the idea of interceptor instead of policies in contract.

    I changed my spring implementation to

        EndpointImpl endpoint = new EndpointImpl(this.cxfBus, flightService);
        endpoint.setAddress("/FlightService");
        endpoint.setWsdlLocation("src/main/resources/wsdls/flightservice_v1r1.wsdl");
        endpoint.getProperties().put("ws-security.callback-handler", new ServerPasswordCallback());
        endpoint.publish();
    

    However, even with that configuration when I deployed my contract it showed 2 wsdl:binding and 2 wsdl:service...

    I found than another way to solve my problem and less couple than the first one, I externalize my policies in a XML file and anotated my service with

    @Policies({
      @Policy(uri="classpath:policies/usernameToken.xml", includeInWSDL=true)
    })
    

    My Spring configuration was reduced to

    EndpointImpl endpoint = new EndpointImpl(this.cxfBus, new FlightServiceImpl());
    endpoint.setAddress("/FlightService");
    endpoint.getProperties().put("ws-security.callback-handler", new ServerPasswordCallback());
    endpoint.publish();
    

    When I deploy me project, finally I see

    <wsdl:service name="FlightServiceImplService">
      <wsdl:port binding="tns:FlightServiceImplServiceSoapBinding" name="FlightServiceImplPort">
        <soap:address location="http://localhost:8080/ws-security/FlightService"/>
      </wsdl:port>
      <wsp:PolicyReference URI="#authenticationPolicy"/>
    </wsdl:service>