soap webservice client使用apache cxf,用于ntlm身份验证

时间:2014-06-30 19:26:54

标签: java cxf soap-client ntlm

我想使用CXF开发一个SOAP客户端来连接SharePoint身份验证方案是NTLM

我在一台机器的登录用户(运行SOAP客户端)可以访问SharePoint的情况下被阻止。 CXF soap客户端始终使用登录用户。 我想指定其他一些用户凭据(而非登录)。

CXF使用in-JDK HttpURLConnection;我读到的关于HttpURLConnection的内容是,当登录用户通过NTLM身份验证时,它会绕过指定的凭据。

在CXF 2.7.11版上尝试了代码。


我尝试过的解决方案:

1)设置管道授权

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(".");

问题:

这不适用于上面指定的方案,因为它始终使用登录的凭据。当我指定无效凭证时,它不会失败。


2)AsyncHTTPConduit

另一种解决方案是使用使用AsyncHTTPConduit代替HttpAsyncClient的{​​{1}}。这是因为HTTP组件不会绕过指定的凭据,并且可以忽略已登录的用户(我已使用HttpURLConnection成功通过测试客户端对此进行了验证。

以下是代码段::

HttpClient

问题:

上面的代码抛出错误:

  

在管道上检测到授权循环。

上面的代码快照显示了现已弃用的 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 ); } 的使用,而是使用了DefaultHttpAsyncClient。但 CloseableHttpAsyncClient未提供为现有CloseableHttpAsyncClient对象指定凭据的方法。不确定如何在此方案中使用CloseableHttpAsyncClient


3)其他解决方案

我尝试的另一个解决方案是使用CloseableHttpAsyncClient来绕过登录的用户身份验证,如上所述here。使用此方法以及上述解决方案#1。这有效/无效凭据的预期工作,代码绕过登录的凭据:)。但是,当我指定无效凭据时,我不会收到HTTP sun.net.www.protocol.http.ntlm.NTLMAuthenticationCallback错误,而是

  

无法发送消息,服务器达到最大重试次数20

我试图避免使用此解决方案,因为它使用java的内部包,并且无法直接确定HTTP 401错误。

我能做些什么才能找到完整的解决方案?

1 个答案:

答案 0 :(得分:0)

试试这个拦截器。这将避免自动验证。

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