Допустим, у нас есть приложение на Spring, оно отправляет rest-запросы в другой сервис по https. Нужно настроить подключение нашего клиента — spring web client к серверу с использованием ssl.
Не нашел явно подходящего мне решения в интернете, поэтому хочу поделиться, как это получилось у меня. В решении мне помогли stackoverflow и пример конфигурирования SSL (ссылки в конце).
Начинаем
Конфигурация
У нас есть файл со свойствами — application.properties или application.yml. В нем мы укажем путь к файлу с сертификатами пользователя и ключами, путь к файлу с доверенными сертификатами (обычно это один файл) и пароли.
ssl: trust-store-location: classpath:key.jks trust-store-password: password trust-store-type: jks key-store-location: classpath:key.jks key-store-password: password key-store-type: jks key-password: password
Создадим класс, который будет соответствовать этой конфигурации.
@Configuration @ConfigurationProperties("ssl") @Data public class SslProperties { Resource trustStoreLocation; String trustStorePassword; String trustStoreType; Resource keyStoreLocation; String keyStorePassword; String keyStoreType; String keyPassword; }
Создадим бин, который будет выполнять “пред-настройку” нашего клиента:
@Configuration @AllArgsConstructor public class HttpConfig { private SslProperties props; @Bean public WebClientCustomizer configureWebclient() { return (WebClient.Builder builder) -> { try { SslContext sslContext = createSslContext(); HttpClient httpClient = HttpClient.create() .secure(sslContextSpec -> sslContextSpec.sslContext(sslContext)); ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient); builder.clientConnector(connector); } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException | UnrecoverableKeyException e) { throw new RuntimeException(e); } }; } private SslContext createSslContext() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { // инициализируем keyManagerFactory для keyStore KeyStore keyStore = KeyStore.getInstance(props.getKeyStoreType()); keyStore.load(props.getKeyStoreLocation().getInputStream(), props.getKeyStorePassword().toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, props.getKeyPassword().toCharArray()); // инициализируем keyManagerFactory для trustStore KeyStore trustStore = KeyStore.getInstance(props.getTrustStoreType()); trustStore.load(props.getTrustStoreLocation().getInputStream(), props.getTrustStorePassword().toCharArray()); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); return SslContextBuilder.forClient() .trustManager(trustManagerFactory) .keyManager(keyManagerFactory) .build(); } }
Использование
Далее подключим этот бин в клиенте:
@Service @RequiredArgsConstructor public class Client { private final WebClient.Builder builder; public void sendRq() { builder.build() .post() // далее логика по отправке запроса и обработке ответа } }
Тестирование
Тестировал эту штуку с mockito. Сконфигурировал примерно так:
public class ClientTest { private Client client; private WebClient.Builder builder; private WebClient webClientMock; @BeforeEach public void setUp() { webClientMock = mock(WebClient.class); builder = mock(WebClient.Builder.class); client = spy(new Client(builder)); } @Test public void testFeatureShouldWork() { when(builder.build()).thenReturn(webClientMock); } }
Заключение
Кажется, что получилась довольно гибкая и удобная конфигурация. Если у вас есть опыт или интересные ссылки для конфигурирования SSL в spring web client, поделитесь, пожалуйста, в комментариях.
Ссылки, которые помогли мне собрать такой вариант:
ссылка на оригинал статьи https://habr.com/ru/articles/737906/