无法在 Spring Web 客户端添加客户端凭据 (clientid/clientsecret):请求处理失败 ... 401 UNAUTHORIZED
failing to add client credentials (clientid/clientsecret) at Spring Webclient: Request processing failed ... 401 UNAUTHORIZED
我正在尝试使用 WebClient 来使用提供令牌的端点。
使用 Postman,它按预期工作。从 postman 导出的 curl 是:
curl --location --request POST 'https://mycomp.url/api/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=xxx' \
--data-urlencode 'client_secret=yyy' \
--data-urlencode 'grant_type=client_credentials'
我正在配置基于上面相同 curl 的 webclient 调用。
这是我的 WebClient 配置:
@Configuration
class ClientConfiguration {
@Bean
fun webClient(): WebClient = WebClient.builder()
.clientConnector(
ReactorClientHttpConnector(
HttpClient.from(
TcpClient
.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.doOnConnected { connection: Connection ->
connection.addHandlerLast(ReadTimeoutHandler(10000, TimeUnit.MILLISECONDS))
connection.addHandlerLast(WriteTimeoutHandler(10000, TimeUnit.MILLISECONDS))
}))
)
.build()
}
这是网络客户端 post 以接收令牌:
@Service
class TokenService(private val webClient: WebClient) {
fun postAsynchronous(): Mono<TokenResponse> = webClient
.post()
.uri(UriComponentsBuilder
.fromHttpUrl("https://mycomp.url")
.path("/api/oauth/token")
.build()
.toUri())
.header("grant_type","client_credentials")
.header("client_id","xxx")
.header("client_secret","yyy")
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.retrieve()
.onStatus(HttpStatus::is4xxClientError) { Mono.error(RuntimeException("4XX Error ${it.statusCode()}")) }
.onStatus(HttpStatus::is5xxServerError) { Mono.error(RuntimeException("5XX Error ${it.statusCode()}")) }
.bodyToMono(TokenResponse::class.java)
}
这是我的build.gradle.kts(相关部分):
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.jetbrains.kotlin.jvm") version "1.4.10"
id("org.jetbrains.kotlin.kapt") version "1.4.10"
kotlin("plugin.spring") version "1.5.20"
id("org.springframework.boot") version "2.4.7"
//kotlin("jvm") version "1.5.30"
id("io.spring.dependency-management") version "1.0.10.RELEASE"
}
val kotlinVersion: String by project
val springVersion: String by project
val projectGroupId: String by project
val projectVersion: String by project
group = projectGroupId
version = projectVersion
repositories {
mavenLocal()
... some internal artifactories
mavenCentral()
}
// add dependencies
dependencies {
kapt(kotlin("stdlib", kotlinVersion))
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect", kotlinVersion))
implementation("org.springframework.boot:spring-boot-dependencies:2.4.7")
implementation("org.springframework.boot:spring-boot-starter:2.4.7")
implementation("org.springframework.boot:spring-boot-starter-web:2.4.7")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.cloud:spring-cloud-starter-openfeign:3.0.3")
implementation("io.github.openfeign:feign-okhttp:10.2.0")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.2")
}
整个异常是:
2021/09/23 17:33:53.123 [http-nio-8080-exec-2] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
2021/09/23 17:33:53.123 [http-nio-8080-exec-2] INFO o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
2021/09/23 17:33:53.124 [http-nio-8080-exec-2] INFO o.s.web.servlet.DispatcherServlet - Completed initialization in 1 ms
2021/09/23 17:33:54.396 [http-nio-8080-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: 4XX Error 401 UNAUTHORIZED] with root cause
java.lang.RuntimeException: 4XX Error 401 UNAUTHORIZED
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:32)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ 401 from POST https://mycomp-url/api/oauth/token [DefaultWebClient]
Stack trace:
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:32)
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:15)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec$StatusHandler.apply(DefaultWebClient.java:693)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec.applyStatusHandlers(DefaultWebClient.java:652)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec.handleBodyMono(DefaultWebClient.java:621)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec.lambda$bodyToMono(DefaultWebClient.java:541)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125)
为了以防万一,我也尝试了其他方法。
我保持 webclient 不变,我只是改变了我发送凭据的方式。
首先,我创建了一个包含所有三个参数的简单 class:
data class TokenRequest(
var grantType: String,
var clientId: String,
var clientSecret: String
)
然后我将webclient.post修改为
fun postAsynchronous(): Mono<TokenResponse> = webClient
.post()
.uri(UriComponentsBuilder
.fromHttpUrl("https://mycomp-url")
.path("/api/oauth/token")
.build()
.toUri())
.body(BodyInserters.fromValue(TokenRequest("client_credentials","xxx", "yyy")))
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.retrieve()
.onStatus(HttpStatus::is4xxClientError) { Mono.error(RuntimeException("4XX Error ${it.statusCode()}")) }
.onStatus(HttpStatus::is5xxServerError) { Mono.error(RuntimeException("5XX Error ${it.statusCode()}")) }
.bodyToMono(TokenResponse::class.java)
我遇到了完全相同的问题:
2021/09/23 18:01:55.994 [http-nio-8080-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: 4XX Error 401 UNAUTHORIZED] with root cause
java.lang.RuntimeException: 4XX Error 401 UNAUTHORIZED
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:32)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ 401 from POST https://mycomp.url/api/oauth/token [DefaultWebClient]
Stack trace:
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:32)
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:15)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec$StatusHandler.apply(DefaultWebClient.java:693)
*** 2021 年 10 月 7 日编辑
通过 Aniket Singla 提议,我遇到了这个新问题:
[reactor-tcp-nio-2] WARN r.n.http.client.HttpClientConnect - [id:9270e5dc-1, L:/10.92.12.165:58268 - R:mycomp-url/x.x.x.x:443] The connection observed an error
org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=com.mycomp.application.models.token.TokenRequest
at org.springframework.web.reactive.function.BodyInserters.unsupportedError(BodyInserters.java:391)
...
[http-nio-8080-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.reactive.function.client.WebClientRequestException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=com.mycomp.application.models.token.TokenRequest; nested exception is org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=com.mycomp.application.models.token.TokenRequest] with root cause
org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=com.mycomp.application.models.token.TokenRequest
根据 Maciej Dobrowolski 的提议,我得到了这个新的例外:
2021/10/07 17:36:29.098 [http-nio-8080-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.core.codec.DecodingException: JSON decoding error: Instantiation of [simple type, class com.mycomp.application.models.token.TokenResponse] value failed for JSON property result due to missing (therefore NULL) value for creator parameter result which is a non-nullable type; nested exception is com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.mycomp.application.models.token.TokenResponse] value failed for JSON property result due to missing (therefore NULL) value for creator parameter result which is a non-nullable type
at [Source: (io.netty.buffer.ByteBufInputStream); line: 8, column: 1] (through reference chain: com.mycomp.application.models.token.TokenResponse["result"])] with root cause
com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.mycomp.application.models.token.TokenResponse] value failed for JSON property result due to missing (therefore NULL) value for creator parameter result which is a non-nullable type
at [Source: (io.netty.buffer.ByteBufInputStream); line: 8, column: 1] (through reference chain: com.mycomp.application.models.token.TokenResponse["result"])
at com.fasterxml.jackson.module.kotlin.KotlinValueInstantiator.createFromObjectWith(KotlinValueInstantiator.kt:112)
*** 已编辑
data class TokenResponse (
val result: String
)
使用 --data-urlencode
curl 选项,您正在向请求的 body 添加参数。在您的 Kotlin 代码中,您没有在请求的 body 中传递相同的数据,而是在 headers.
中传递相同的数据
你应该做的(模仿邮递员行为)是通过使用 BodyInserters
在请求 body 中传递 grant_type
、client_id
、client_secret
,像这样:
webClient
.post()
.uri(UriComponentsBuilder
.fromHttpUrl("https://mycomp.url")
.path("/api/oauth/token")
.build()
.toUri())
.body(BodyInserters.fromFormData("grant_type", "client_credentials")
.with("client_id", "xxx")
.with("client_secret", "yyy"))
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.retrieve()
// ...
在 headers 中提供 url 编码数据将不起作用,您只需要在 headers 中告知您将使用“application/x-www-form-urlencoded”作为内容类型, else 将由 webclient 负责将 body 转换为 url 编码形式。对您的 postAsynchronous 方法进行了一些更改,应该可以解决您的问题。
fun postAsynchronous(): Mono<TokenResponse> = webClient
.post()
.uri(UriComponentsBuilder
.fromHttpUrl("https://des-sts-int.mbi.cloud.ihf")
.path("/api/oauth/token")
.build()
.toUri())
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.body(BodyInserters.fromFormData("grant_type", "client_credentials")
.with("client_id", "xxx")
.with("client_secret", "yyy")) )
.retrieve()
.onStatus(HttpStatus::is4xxClientError) { Mono.error(RuntimeException("4XX Error ${it.statusCode()}")) }
.onStatus(HttpStatus::is5xxServerError) { Mono.error(RuntimeException("5XX Error ${it.statusCode()}")) }
.bodyToMono(TokenResponse::class.java)
我正在尝试使用 WebClient 来使用提供令牌的端点。 使用 Postman,它按预期工作。从 postman 导出的 curl 是:
curl --location --request POST 'https://mycomp.url/api/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=xxx' \
--data-urlencode 'client_secret=yyy' \
--data-urlencode 'grant_type=client_credentials'
我正在配置基于上面相同 curl 的 webclient 调用。
这是我的 WebClient 配置:
@Configuration
class ClientConfiguration {
@Bean
fun webClient(): WebClient = WebClient.builder()
.clientConnector(
ReactorClientHttpConnector(
HttpClient.from(
TcpClient
.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.doOnConnected { connection: Connection ->
connection.addHandlerLast(ReadTimeoutHandler(10000, TimeUnit.MILLISECONDS))
connection.addHandlerLast(WriteTimeoutHandler(10000, TimeUnit.MILLISECONDS))
}))
)
.build()
}
这是网络客户端 post 以接收令牌:
@Service
class TokenService(private val webClient: WebClient) {
fun postAsynchronous(): Mono<TokenResponse> = webClient
.post()
.uri(UriComponentsBuilder
.fromHttpUrl("https://mycomp.url")
.path("/api/oauth/token")
.build()
.toUri())
.header("grant_type","client_credentials")
.header("client_id","xxx")
.header("client_secret","yyy")
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.retrieve()
.onStatus(HttpStatus::is4xxClientError) { Mono.error(RuntimeException("4XX Error ${it.statusCode()}")) }
.onStatus(HttpStatus::is5xxServerError) { Mono.error(RuntimeException("5XX Error ${it.statusCode()}")) }
.bodyToMono(TokenResponse::class.java)
}
这是我的build.gradle.kts(相关部分):
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.jetbrains.kotlin.jvm") version "1.4.10"
id("org.jetbrains.kotlin.kapt") version "1.4.10"
kotlin("plugin.spring") version "1.5.20"
id("org.springframework.boot") version "2.4.7"
//kotlin("jvm") version "1.5.30"
id("io.spring.dependency-management") version "1.0.10.RELEASE"
}
val kotlinVersion: String by project
val springVersion: String by project
val projectGroupId: String by project
val projectVersion: String by project
group = projectGroupId
version = projectVersion
repositories {
mavenLocal()
... some internal artifactories
mavenCentral()
}
// add dependencies
dependencies {
kapt(kotlin("stdlib", kotlinVersion))
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect", kotlinVersion))
implementation("org.springframework.boot:spring-boot-dependencies:2.4.7")
implementation("org.springframework.boot:spring-boot-starter:2.4.7")
implementation("org.springframework.boot:spring-boot-starter-web:2.4.7")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.cloud:spring-cloud-starter-openfeign:3.0.3")
implementation("io.github.openfeign:feign-okhttp:10.2.0")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.2")
}
整个异常是:
2021/09/23 17:33:53.123 [http-nio-8080-exec-2] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
2021/09/23 17:33:53.123 [http-nio-8080-exec-2] INFO o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
2021/09/23 17:33:53.124 [http-nio-8080-exec-2] INFO o.s.web.servlet.DispatcherServlet - Completed initialization in 1 ms
2021/09/23 17:33:54.396 [http-nio-8080-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: 4XX Error 401 UNAUTHORIZED] with root cause
java.lang.RuntimeException: 4XX Error 401 UNAUTHORIZED
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:32)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ 401 from POST https://mycomp-url/api/oauth/token [DefaultWebClient]
Stack trace:
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:32)
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:15)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec$StatusHandler.apply(DefaultWebClient.java:693)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec.applyStatusHandlers(DefaultWebClient.java:652)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec.handleBodyMono(DefaultWebClient.java:621)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec.lambda$bodyToMono(DefaultWebClient.java:541)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125)
为了以防万一,我也尝试了其他方法。
我保持 webclient 不变,我只是改变了我发送凭据的方式。
首先,我创建了一个包含所有三个参数的简单 class:
data class TokenRequest(
var grantType: String,
var clientId: String,
var clientSecret: String
)
然后我将webclient.post修改为
fun postAsynchronous(): Mono<TokenResponse> = webClient
.post()
.uri(UriComponentsBuilder
.fromHttpUrl("https://mycomp-url")
.path("/api/oauth/token")
.build()
.toUri())
.body(BodyInserters.fromValue(TokenRequest("client_credentials","xxx", "yyy")))
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.retrieve()
.onStatus(HttpStatus::is4xxClientError) { Mono.error(RuntimeException("4XX Error ${it.statusCode()}")) }
.onStatus(HttpStatus::is5xxServerError) { Mono.error(RuntimeException("5XX Error ${it.statusCode()}")) }
.bodyToMono(TokenResponse::class.java)
我遇到了完全相同的问题:
2021/09/23 18:01:55.994 [http-nio-8080-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: 4XX Error 401 UNAUTHORIZED] with root cause
java.lang.RuntimeException: 4XX Error 401 UNAUTHORIZED
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:32)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ 401 from POST https://mycomp.url/api/oauth/token [DefaultWebClient]
Stack trace:
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:32)
at com.mycomp.security.TokenService$postAsynchronous.apply(TokenService.kt:15)
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec$StatusHandler.apply(DefaultWebClient.java:693)
*** 2021 年 10 月 7 日编辑
通过 Aniket Singla 提议,我遇到了这个新问题:
[reactor-tcp-nio-2] WARN r.n.http.client.HttpClientConnect - [id:9270e5dc-1, L:/10.92.12.165:58268 - R:mycomp-url/x.x.x.x:443] The connection observed an error
org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=com.mycomp.application.models.token.TokenRequest
at org.springframework.web.reactive.function.BodyInserters.unsupportedError(BodyInserters.java:391)
...
[http-nio-8080-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.reactive.function.client.WebClientRequestException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=com.mycomp.application.models.token.TokenRequest; nested exception is org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=com.mycomp.application.models.token.TokenRequest] with root cause
org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=com.mycomp.application.models.token.TokenRequest
根据 Maciej Dobrowolski 的提议,我得到了这个新的例外:
2021/10/07 17:36:29.098 [http-nio-8080-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.core.codec.DecodingException: JSON decoding error: Instantiation of [simple type, class com.mycomp.application.models.token.TokenResponse] value failed for JSON property result due to missing (therefore NULL) value for creator parameter result which is a non-nullable type; nested exception is com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.mycomp.application.models.token.TokenResponse] value failed for JSON property result due to missing (therefore NULL) value for creator parameter result which is a non-nullable type
at [Source: (io.netty.buffer.ByteBufInputStream); line: 8, column: 1] (through reference chain: com.mycomp.application.models.token.TokenResponse["result"])] with root cause
com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.mycomp.application.models.token.TokenResponse] value failed for JSON property result due to missing (therefore NULL) value for creator parameter result which is a non-nullable type
at [Source: (io.netty.buffer.ByteBufInputStream); line: 8, column: 1] (through reference chain: com.mycomp.application.models.token.TokenResponse["result"])
at com.fasterxml.jackson.module.kotlin.KotlinValueInstantiator.createFromObjectWith(KotlinValueInstantiator.kt:112)
*** 已编辑
data class TokenResponse (
val result: String
)
使用 --data-urlencode
curl 选项,您正在向请求的 body 添加参数。在您的 Kotlin 代码中,您没有在请求的 body 中传递相同的数据,而是在 headers.
你应该做的(模仿邮递员行为)是通过使用 BodyInserters
在请求 body 中传递 grant_type
、client_id
、client_secret
,像这样:
webClient
.post()
.uri(UriComponentsBuilder
.fromHttpUrl("https://mycomp.url")
.path("/api/oauth/token")
.build()
.toUri())
.body(BodyInserters.fromFormData("grant_type", "client_credentials")
.with("client_id", "xxx")
.with("client_secret", "yyy"))
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.retrieve()
// ...
在 headers 中提供 url 编码数据将不起作用,您只需要在 headers 中告知您将使用“application/x-www-form-urlencoded”作为内容类型, else 将由 webclient 负责将 body 转换为 url 编码形式。对您的 postAsynchronous 方法进行了一些更改,应该可以解决您的问题。
fun postAsynchronous(): Mono<TokenResponse> = webClient
.post()
.uri(UriComponentsBuilder
.fromHttpUrl("https://des-sts-int.mbi.cloud.ihf")
.path("/api/oauth/token")
.build()
.toUri())
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.body(BodyInserters.fromFormData("grant_type", "client_credentials")
.with("client_id", "xxx")
.with("client_secret", "yyy")) )
.retrieve()
.onStatus(HttpStatus::is4xxClientError) { Mono.error(RuntimeException("4XX Error ${it.statusCode()}")) }
.onStatus(HttpStatus::is5xxServerError) { Mono.error(RuntimeException("5XX Error ${it.statusCode()}")) }
.bodyToMono(TokenResponse::class.java)