Spring HTTP invoker с https и неподписанным сертификатом
Мы использовали Springs HttpInvoker уже несколько недель, и это работает как шарм. Из моего внешнего (веб) приложения я подключаюсь к пользовательскому сервису бэкэнда следующим образом:
<bean id="userService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="http://${backend.host}/backend-ws/remoting/UserService"/>
<property name="serviceInterface" value="com...service.UserService"/>
</bean>
UserService затем красиво внедряется в наши классы интерфейса.
Теперь мы внедряем это на надлежащий (WAS7) сервер, и существует требование использовать SSL (https). Итак, я изменяю http (из serviceUrl) на https, но затем я получаю:
org.springframework.remoting.RemoteAccessException: Could not access HTTP invoker remote service at [https://pbp-dev.dev.echonet/public/backend-ws/remoting/ParameterHelper]; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
что имеет смысл, поскольку сертификат, установленный на сервере (где работает WAS), не подписан центром сертификации.
У нас уже есть некоторый опыт в этом, поскольку на том же WAS работает веб-сервис; для этого мы используем cxf и сгенерировали файл jks (с keytool), который находится в клиентском приложении и настроен следующим образом:
<http:conduit name="https://serverurl/.*">
<http:tlsClientParameters secureSocketProtocol="SSL" disableCNCheck="false">
<sec:trustManagers>
<sec:keyStore type="jks" password="pass123" resource="trust.jks"/>
</sec:trustManagers>
</http:tlsClientParameters>
Я думаю, для Http Invoker нам нужно сделать что-то подобное, но мы не знаем, как использовать этот trust.jks в invoker.
Одна вещь, которую я нашел, это использовать другой requestExecutor; как это:
<bean id="userService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="https://${backend.host}/backend-ws/remoting/UserService"/>
<property name="serviceInterface" value="com...service.UserService"/>
<property name="httpInvokerRequestExecutor">
<bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor" />
</property>
</bean>
После этого я больше не получаю ошибку сертификата, но userService, кажется, не создается с тех пор, как я получаю:
NoSuchBeanDefinitionException: No matching bean of type [com...service.UserService] found for dependency
2 ответа
Если вы смешаете то, что можете найти здесь (http://stackru.com/questions/5947162/https-and-self-signed-certificate-issue), чтобы сконфигурировать возвращаемый HttpClient с предварительно настроенным SSLSocketFactory, вы можете изменить имя хоста. верификатор фабрики сокетов для принятия сертификата, что-то похожее на это:
xxx.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) { println("bypassing ssl cert handshaking as configured for self signed cert."); return true; }
});
Согласно вашей конфигурации, кроме использования CommonsHttpInvokerRequestExecutor вы также должны настроить используемый HTTPClient и фабрику сокетов SSL
Я знаю, что это, вероятно, не полностью отвечает на ваш вопрос, но это отправная точка для других поисков! Удачи и не забудьте опубликовать окончательное решение.
Вы можете попробовать что-то следующим образом:
Сначала напишите собственный класс org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor
:
package com.myorg.proid.sample;
import static org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.X509Certificate;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor;
/**
* @author visruth
*
*/
public class CustomHttpComponentsHttpInvokerRequestExecutor extends
HttpComponentsHttpInvokerRequestExecutor {
public CustomHttpComponentsHttpInvokerRequestExecutor() {
skipSecurityChecking();
}
@SuppressWarnings("deprecation")
private void skipSecurityChecking() {
// HttpClient from super class.
HttpClient httpClient = getHttpClient();
TrustStrategy trustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] certificate,
String authType) {
return true;
}
};
try {
httpClient
.getConnectionManager()
.getSchemeRegistry()
.register(
new Scheme("https", 80, new SSLSocketFactory(
trustStrategy,
ALLOW_ALL_HOSTNAME_VERIFIER)));
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
}
}
и ссылаться на этот класс в вашем XML- файле вместо org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor
как
<bean id="userService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="https://${backend.host}/backend-ws/remoting/UserService"/>
<property name="serviceInterface" value="com...service.UserService"/>
<property name="httpInvokerRequestExecutor">
<bean class="com.myorg.proid.sample.CustomHttpComponentsHttpInvokerRequestExecutor" />
</property>
</bean>