Search code examples
authenticationcxfjax-wspolicy

JAX-WS Client CXF WS-Policy Issue


I have to implement a JAX-WS-Client which consumes a service from an external partner. I use Apache CXF. The service defines two WS-Policies for authentication in the wsdl - KerberosToken and UsernameToken. Since the service is from an external partner it cant be changed.

The problem: Authentication fails at Kerberos-Authentication since I want to use the simple UsernameToken-Authentication.

WSLD-Policy-Part:

<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" 
  xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
  wsu:Id="SecurityServiceUsernameUnsecureTransportPolicy">
  <wsp:ExactlyOne>
  <wsp:All>
    <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
    <wsp:Policy>
      <sp:KerberosToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
      <wsp:Policy>
        <sp:WssGssKerberosV5ApReqToken11/>
      </wsp:Policy>
      </sp:KerberosToken>
    </wsp:Policy>
    </sp:SupportingTokens>
  </wsp:All>
  <wsp:All>
    <sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
    <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>

Setting Username/Password at CXF-Endpoint:

public void addAuthenticationProperties( Endpoint endpoint ) {
  endpoint.put( SecurityConstants.USERNAME, userName );
  endpoint.put( SecurityConstants.PASSWORD, password );
}

As I understand the -Tag means, that if any (excatly one) of this policies is fullfilled move on. But CXF does not even try to fullfill the UsernameToken - Policy.

If I remove the -Block for KerberosToken authentication works fine, but in production this is not possible.

Any hints? If there is an error in the wsdl or in my approach please let me know. And please be detailed - I am a noob in this field.

Thanks in advance!


Solution

  • Some days later I found a workaround for this problem - wouldn't call it solution ;-)

    At first the old version: CXF generated a class ExampleWS_Service and a corresponding interface ExampleWS. So I used the ExampleWS_Service to set everthing up and invoke the service:

    // creating Port
    URL url = new URL( config.getSchema(), config.getHost(), config.getPort(), config.getPath() );
    
    ExampleWS_Service service = new ExampleWS_Service( url );
    ExampleWS port = service.getExampleWSPort();
    
    // Adding authentication-info
    Client client = ClientProxy.getClient( port );
    Endpoint cxfEndpoint = client.getEndpoint();
    
    cxfEndpoint.put( SecurityConstants.USERNAME, userName );
    cxfEndpoint.put( SecurityConstants.PASSWORD, password );
    
    ...
    // invoke service
    port.doSomething( [data] );
    

    The workaround: I switched to JaxWsProxyFactoryBean and setting everthing up. In this case a WSS4JOutInterceptor is needed to put authentication information in the request-header. Some code:

    URL url = new URL( config.getSchema(), config.getHost(), config.getPort(), config.getPath() );
    
    JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
    factory.setServiceClass( ExampleWS.class );
    factory.setAddress( url.toString() );
    factory.setBindingId( "http://schemas.xmlsoap.org/wsdl/soap12/" );
    
    ExampleWS port = ( ExampleWS )factory.create();
    
    // Adding authentication-info using WSS4JOutInterceptor
    Client client = ClientProxy.getClient( port );
    Endpoint cxfEndpoint = client.getEndpoint();
    
    Map<String, Object> outProps = new HashMap<>();
    outProps.put( WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN );
    outProps.put( WSHandlerConstants.USER, userName );
    outProps.put( WSHandlerConstants.PASSWORD_TYPE, passwordType );
    outProps.put( WSHandlerConstants.PW_CALLBACK_REF, [Instance of javax.security.auth.callback.CallbackHandler] );
    
    WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor( outProps );
    cxfEndpoint.getOutInterceptors().add( wssOut );
    
    ...
    // invoke service
    port.doSomething( [data] );
    

    I don't know why exactly this workaround actually work, but it does :-) Perhaps someone of you can give me so enlightenment.