我想开发一个使用 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/