CXF Failover Conduit, измененный на лету - гарантирует тот же сеанс SSL и безопасность потока клиента? - PullRequest
0 голосов
/ 19 июня 2019

Я хотел бы реализовать функцию отказоустойчивости CXF для обоих клиентов JAX-WS / RS в нашем приложении, которые должны вызывать удаленные веб-сервисы через HTTPS с использованием клиентских сертификатов.Имеются 2 удаленных сервера: основной + альтернативный.

Я немного растерян, как гарантировать такую ​​же функциональность (с правильными параметрами TLS / сеанс SSL) после перехода на другой ресурс.

JAX-WS клиент

Существует 6 ClientServices, которые extend AbstractClientServiceImpl и используют один и тот же PortType wsClient bean-компонент и один и тот же basePath удаленного сервера, однако они устанавливают их как последнюю часть Uri службыс их собственным String getEndpointUrl(){ return "X";} методом.

BasePath: https://remote1.server.com:443/api

Альтернативные адреса: https://remote2.server.com:443/api

Пожалуйста, посмотрите на код- ClientEndpointAddressInterceptor.С помощью этого перехватчика я могу объединить basePath + lastUriPart и вызвать нужную конечную точку для определенного ClientService - даже когда происходит аварийное переключение.Например:

целевая конечная точка для ClientService1.class https://remote1.server.com:443/api/service1

целевая конечная точка для ClientService2.class: https://remote1.server.com:443/api/service2

Я боролся справильные настройки / конфигурация для 2 недель.Если я не добавлю tlsClientParameters или HttpClientPolicy к extensor примерно так, то после того, как произойдет аварийное переключение, я не смог бы увидеть какие-либо настройки TLS для вновь созданного канала!

// ssl settings
endpointInfo.addExtensor(tlsClientParameters);

Я не знаю, является ли это правильным способом сделать это, но с этим хакерским обходным путем мне удалось бы "предоставить" те же самые tlsClientParams для 2удаленные вызовы (основной + альтернативный адрес удаленного сервера) - SAN сертификата клиента имеет DNS-имена обоих серверов.

Технология: SpringBoot v2.1 + CXF 3.3.0 + Tomcat8.5

@Bean
public PortType wsClient(Properties properties,
                                        TLSClientParameters tlsClientParameters,
                                        LoggingFeature loggingFeature,
                                        ClientEndpointAddressFeature clientEndpointAddressFeature) {
        return createClient(properties, huTlsClientParameters, loggingFeature, 
    createFailoverFeature(properties.getFailover().getAddresses(), properties.getFailover().getRetryDelay()), 
                clientEndpointAddressFeature);
}

private FailoverFeature createFailoverFeature(String[] alternateAddresses, long failOverRetryDelay) {

    final FailoverFeature failOverFeature = new FailoverFeature();
    final SequentialStrategy strategy = new SequentialStrategy();
    strategy.setAlternateAddresses(Arrays.asList(alternateAddresses));
    strategy.setDelayBetweenRetries(failOverRetryDelay);
    failOverFeature.setStrategy(strategy);
    return failOverFeature;
}

private PortType createClient(Properties properties, TLSClientParameters tlsClientParameters, WebServiceFeature... features) {
    final Service service = new Service();
    final PortType client = service.getPortType(features);
    final Client clientProxy = ClientProxy.getClient(client);
    final EndpointInfo endpointInfo = clientProxy.getEndpoint().getEndpointInfo();

    final HTTPClientPolicy httpClientPolicy = Optional.ofNullable(endpointInfo.getExtensor(HTTPClientPolicy.class))
            .orElseGet(() -> {
                // if there is no XYFeature, policy has to be initialized at this point
                final HTTPClientPolicy policy = new HTTPClientPolicy();
                policy.setAccept(HuHttpHeaders.HEADER_ACCEPT_VALUE);
                endpointInfo.addExtensor(policy);
                return policy;
            });
    // timeout settings
    httpClientPolicy.setConnectionTimeout(properties.getConnectionTimeout());
    httpClientPolicy.setReceiveTimeout(properties.getReadTimeout());
    // set content-length by default
    httpClientPolicy.setAllowChunking(false);
    // ssl settings
    endpointInfo.addExtensor(tlsClientParameters);
    // set global requestContext
    setRequestContext((BindingProvider) client, properties.getUrl());
    return client;
}


private void setRequestContext(BindingProvider bp, String server) {
    bp.getRequestContext().put(Message.ENDPOINT_ADDRESS, server);
    bp.getRequestContext().put(ClientImpl.THREAD_LOCAL_REQUEST_CONTEXT, true);
    bp.getRequestContext().put(Message.SCHEMA_VALIDATION_ENABLED, true);
    bp.getRequestContext().put(BindingProvider.SOAPACTION_USE_PROPERTY, true);
}


public class ClientEndpointAddressOutInterceptor extends AbstractPhaseInterceptor<Message> {
    public ClientEndpointAddressOutInterceptor() {
        super(Phase.PREPARE_SEND);
        addBefore(MessageSenderInterceptor.class.getName());
    }

    @Override
    public void handleMessage(Message message) throws Fault {
        final String previousEndpointAddress = (String) message.get(Message.ENDPOINT_ADDRESS);
        final String lastUriPath = (String) message.get("lastUriPath");
        message.put(Message.ENDPOINT_ADDRESS, previousEndpointAddress + lastUriPath);
   }
}
public abstract class AbstractClientServiceImpl implements ClientService {
        public AbstractClientServiceImpl(PortType PortType) {
            this.portType = portType;
        }
        @Override
        public HttpStatus sendRequest(String xmlData) {
            ...
            final BindingProvider bindingProvider = (BindingProvider) this.portType;
            try {
            // set http header for this particular request          
            // also store bindingProvider.getRequestContext().put("lastUriPath", getEndpointUrl()); 
            HttpHeaderUtil.setHttpHeader(getSoapActionUrl(), bindingProvider, getEndpointUrl());
                execute(xmlData, createSoapHeader());
            } catch (Exception ex) {
                ...
            }
            ...
        }
        // last uri part 
        protected abstract String getEndpointUrl();
        // execute is responsible for calling a particular service. e.g: in ClientService1.class portType.callService1(xmlData);
        protected abstract void execute(String xmlData, TransactionHeader transactionHeader);
}

Вопросы

Клиент JAX-WS

  • что происходит с ранее установленным параметром (thread-local-reports, true) послепроисходит аварийное переключение?Будут ли следующие сервисные вызовы, вызываемые классом 6 ClientServices, впоследствии оставаться поточно-ориентированными?
  • У меня есть требование, что я должен использовать механизм повторного использования сеанса SSL.Если функция аварийного переключения CXF удаляет канал и создает новый, то как я могу применить это снова после аварийного переключения?Или Tomcat каким-то образом справляется с этим, и нет необходимости беспокоиться об этом?Я не эксперт в CXF, и я не нашел много информации, связанной с сеансом SSL, в сайте / списке рассылки CXF.

JAX-RS клиент

На самом деле те же 2 проблемы / вопросы, которые рассмотрены выше для клиента JAX-WS.

Единственное отличие состоит в том, что RS имеет 3 метода вызова клиента, использующих тот же экземпляр клиента, объявленный как

private WebClient webClient(){
   final JAXRSClientFactoryBean clientFactoryBean = new JAXRSClientFactoryBean();
   clientFactoryBean.setThreadSafe(true);
   final WebClient webClient = clientFactoryBean.createWebClient();
        final ClientConfiguration config = WebClient.getConfig(webClient);
        config.getRequestContext().put(HTTPConduit.NO_IO_EXCEPTIONS, Boolean.TRUE);
        // ssl settings
    config.getEndpoint().getEndpointInfo().addExtensor(tlsClientParameters);
    return webClient;
}

Заранее спасибо за помощь.

...