4

I want to develop a SOAP client using CXF to connect to SharePoint. The authentication scheme is NTLM.

I am blocked on a scenario where the logged-in user of a machine (on which the SOAP client is being run) has access to SharePoint. The CXF soap client always uses the logged-in user. I want to specify some other user credentials (not the logged-in).

As CXF uses in-JDK HttpURLConnection; and what I have read about HttpURLConnection is, it bypasses the specified credentials when the logged-in user is NTLM authenticated.

Codes were tried on CXF version 2.7.11.


Solutions that I have tried out:

1) Setting Conduit authorization

String username = "user";     
String password = "password";    

JaxWsProxyfactoryBean factory1 = new JaxWsProxyfactoryBean();    
factory1.setServiceClass(WebsSoap.class);    
factory1.setAddress(url);    
factory1.setUsername(username);    
factory1.setPassword(password);

WebsSoap service = (WebsSoap) factory1.create();    
Client client = ClientProxy.getClient(service);    

HTTPconduit conduit = (HTTPconduit) client.getconduit();    
conduit.getAuthorization().setAuthorizationType("NTLM");    
conduit.getAuthorization().setUserName(username);    
conduit.getAuthorization().setPassword(password);

HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();  
httpClientPolicy.setConnectionTimeout(36000);  
httpClientPolicy.setAllowChunking(false);  
conduit.setClient(httpClientPolicy);  
        
service.getWeb(".");

Problem:

This does not work for the scenario specified above, as it always uses the logged-in credentials. And when I specify invalid credentials, it does not fail.


2) AsyncHTTPConduit

Another solution is to use AsyncHTTPConduit that uses HttpAsyncClient instead of HttpURLConnection. This is beacuse HTTP components do not bypass specified credentials and logged-in user can be ignored (I have successfully verified this with a test client using HttpClient).

Below is the code snippet::

Bus bus = BusFactory.getDefaultBus();    
bus.setProperty( "use.async.http.conduit", "true" );
 
Client client = ClientProxy.getClient( service );    
HTTPConduit http = (HTTPConduit)client.getConduit();    
if ( http instanceof AsyncHTTPConduit ) {    
    AsyncHTTPConduit conduit = (AsyncHTTPConduit)http;    
    DefaultHttpAsyncClient defaultHttpAsyncClient;    
    try {    
        defaultHttpAsyncClient = conduit.getHttpAsyncClient();    
    }    
    catch ( IOException exception ) {    
        throw new RuntimeException( exception );    
    }    
    defaultHttpAsyncClient.getCredentialsProvider().setCredentials( AuthScope.ANY,
                        new NTCredentials( "username", "password", "", "domain" ) );         
    conduit.getClient().setAllowChunking( false );
    conduit.getClient().setAutoRedirect( true );
}

Problem:

Above code throws error:

Authorization loop detected on conduit.

The above code snapshot shows the usage of DefaultHttpAsyncClient which is deprecated now and CloseableHttpAsyncClient is to be used instead. But CloseableHttpAsyncClient does not provide a way to specify credentials to an already existing CloseableHttpAsyncClient object. Not sure how to use CloseableHttpAsyncClient in this scenario.


3) Other solutions

The other solution that I tried out is to use sun.net.www.protocol.http.ntlm.NTLMAuthenticationCallback, to bypass logged-in user authentication, as mentioned here. Use this approach along with solution #1 mentioned above. This works as expected for valid/invalid credentials, and the code bypasses the logged-in credentials :). But when I specify invalid credentials, I do not get HTTP 401 error, instead I get

Could not send message, server reached max retries 20

I am trying to avoid this solution because it uses java’s internal package and there is no way to determine HTTP 401 error directly.

What can I do to arrive at a complete solution?

Community
  • 1
  • 1
Vinay
  • 41
  • 1
  • 3

1 Answers1

0

Try this interceptor. This will avoid automatic authentication.

public class DisableAutomaticNTLMAuthOutInterceptor extends AbstractPhaseInterceptor<Message>
{
    private boolean isFieldsAvailable;

    private Field tryTransparentNTLMProxyField;

    private Field tryTransparentNTLMServerField;

    public DisableAutomaticNTLMAuthOutInterceptor() {
        super(Phase.PRE_STREAM);

        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            public Void run() {
                try {
                    DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMServerField = HttpURLConnection.class.getDeclaredField("tryTransparentNTLMServer");
                    DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMServerField.setAccessible(true);

                    DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMProxyField = HttpURLConnection.class.getDeclaredField("tryTransparentNTLMProxy");
                    DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMProxyField.setAccessible(true);
                    DisableAutomaticNTLMAuthOutInterceptor.this.isFieldsAvailable = true;

                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public void handleMessage(final Message message) throws Fault {
        if (this.isFieldsAvailable)
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Void run() {
                    try {
                        Object httpConnection = message.get("http.connection");
                        if (httpConnection != null) {
                            DisableAutomaticNTLMAuthOutInterceptor.this.processHttpConnection(message.get("http.connection"));
                        }
                    } catch (Throwable t) {
                        t.printStackTrace();
                    }
                    return null;
                }
            });

    }

    private void processHttpConnection(Object httpConnection) throws IllegalArgumentException, IllegalAccessException {

        if (HttpURLConnection.class.isAssignableFrom(httpConnection.getClass())) {
            tryTransparentNTLMServerField.set(httpConnection, Boolean.FALSE);
            tryTransparentNTLMProxyField.set(httpConnection, Boolean.FALSE);
        } else {
            Field tempField = null;
            for (Field field : httpConnection.getClass().getDeclaredFields()) {
                if (HttpURLConnection.class.isAssignableFrom(field.getType())) {
                    field.setAccessible(true);
                    tempField = field;
                    break;
                }
            }
            if (tempField != null) {
                processHttpConnection(tempField.get(httpConnection));
            }
        }
    }
}
SANN3
  • 9,459
  • 6
  • 61
  • 97