Допустим, у нас есть приложение на 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, поделитесь, пожалуйста, в комментариях.

Ссылки, которые помогли мне собрать такой вариант:

Комментарии (1)


  1. ultrinfaern
    26.05.2023 15:17
    +1

    Правильный ответ - никак, так как WebClient это обвертка над HttpClient. И конфигурировать нужно то клиент, который вы будете использовать (встроенный, Apache, OkHttp, ...)