java - 使用 apache cxf 的 soap web 服务客户端,用于 ntlm 身份验证

标签 java cxf soap-client ntlm

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

在某台计算机(正在运行 SOAP 客户端)的登录用户有权访问 SharePoint 的情况下,我被阻止了。 CXF soap 客户端始终使用登录用户。 我想指定一些其他用户凭据(不是已登录的)。

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

代码已在 CXF 版本 2.7.11 上进行了尝试。


我尝试过的解决方案:

1) 设置Conduit权限

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 而不是 HttpURLConnection。这是因为 HTTP 组件不会绕过指定的凭据,并且可以忽略已登录的用户(我已经使用 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 );
}

问题:

以上代码抛出错误:

Authorization loop detected on conduit.

上面的代码快照显示了 DefaultHttpAsyncClient 的用法,现在已弃用,将使用 CloseableHttpAsyncClient 代替。但是 CloseableHttpAsyncClient 没有提供一种方法来为已经存在的 CloseableHttpAsyncClient 对象指定凭据。不确定如何在这种情况下使用 CloseableHttpAsyncClient


3) 其他解决方案

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

Could not send message, server reached max retries 20

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

我该怎么做才能获得完整的解决方案?

最佳答案

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

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

关于java - 使用 apache cxf 的 soap web 服务客户端,用于 ntlm 身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24497494/

相关文章:

java - 当调用break时,while循环不会中断

java - Android UTF-8 编码不起作用?

java - PLC-IT通讯

java - 某些字符的正则表达式

java - JAX-RS Web 应用程序中的 Apache CXF OAuthDataProvider

java - 轴故障 : Transport error: 415 Error in AXIS2 client(JAVA)

java - 使用 spring boot # SOAP Service 并行调用 SOAP 服务

php - 如何使SoapClient更正常地失败?

java - 更改 CXF Web 服务上的响应字符集

java - 注解@XmlElement 只写?