WebFlux: повторите попытку WebClient с авторизацией OAuth2 - PullRequest
0 голосов
/ 28 апреля 2020

Мне нужно запросить новый токен авторизации с использованием гранта учетных данных клиента (OAuth2) от KeyCloak после сбоя GET-запроса с WebClient из-за 401 Unauthorized WebClientResponseException или исключения тайм-аута. Поэтому я использую метод Mono.retryWhen(...). Самый популярный ответ на SO гласит здесь , что его можно решить с помощью фильтра, который использует TokenProvider. И что в будущем выпуске Spring-Security будет более элегантное решение. К сожалению, я не смог заставить этот код работать, и не нашел элегантного решения.

WebClientConfig-Bean:

@Bean
WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations, ReactiveOAuth2AuthorizedClientService authorizedClientService) {
    ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrations, authorizedClientService));
    oauth.setDefaultClientRegistrationId("keycloak");
    return WebClient.builder()
            .filter(oauth)
            .build();
}

Реализация WebClient

public <T> Mono<T> callServiceOAuth2(String uri, Class<T> type, String logPrefix) {
    return webClient.get()
            .uri(uri)
            .retrieve()
            .bodyToMono(type)
            .publishOn(Schedulers.boundedElastic())
            .doOnRequest(s -> LOGGER.info("{} Send request on URL: {}", logPrefix, uri))
            .retryWhen(Retry.backoff(5, Duration.ofSeconds(5))
                    .scheduler(Schedulers.boundedElastic())
                    .doAfterRetry( retry -> LOGGER.error("{} Retry: {} on {}, Error Message: {}",logPrefix, retry.totalRetries()+1, uri, retry.failure())))
            .elapsed(Schedulers.boundedElastic())
            .doOnNext(tuple -> LOGGER.info("{} Service response time: {}s", logPrefix, Duration.ofMillis(tuple.getT1()).toSeconds()))
            .map(Tuple2::getT2) // after outputting the measurement, return the data only
            .onErrorResume(fallback -> {LOGGER.error("{} Retry Exceeded, Exception: {}", logPrefix, fallback.toString()); return Mono.empty();})                ;
}

POM зависимости

spring-boot-starter-parent: 2.2.6.RELEASE
spring-boot-starter-webflux: 2.2.6.RELEASE
spring-security-oauth2-client: 5.3.1.RELEASE
spring-security-config: 5.3.1.RELEASE

application.yml

spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: 'testclient_xyz'
            client-secret: 'abc'
            authorizationGrantType: client_credentials
        provider:
          keycloak:
            token-uri: https://abc.de/auth/realms/my_realm/protocol/openid-connect/token
...