简体   繁体   中英

soap webservice client using apache cxf, for ntlm authentication

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?

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));
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM