spring-oauth2 - Spring oauth2 webclient ClientAuthorizationException:[server_error]
问题描述
我正在尝试利用 Spring 5 Webclient 和内置的 oauth2 安全功能。
@Bean("oauth2WebClient")
public WebClient oauth2WebClient(final ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager) {
final ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(reactiveOAuth2AuthorizedClientManager);
serverOAuth2AuthorizedClientExchangeFilterFunction.setDefaultClientRegistrationId("app");
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(new ClassPathResource("app.jks").getFile()), "password".toCharArray());
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(new FileInputStream(new ClassPathResource("app.jks").getFile()), "password".toCharArray());
// Set up key manager factory to use our key store
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "password".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
SslContext sslContext = SslContextBuilder.forClient()
.keyManager(keyManagerFactory)
.trustManager(trustManagerFactory)
.build();
//
//
// List<Certificate> certificateList = Collections.list(trustStore.aliases())
// .stream()
// .filter(t -> {
// try {
// return trustStore.isCertificateEntry(t);
// } catch (KeyStoreException e1) {
// throw new RuntimeException("Error reading truststore", e1);
// }
// })
// .map(t -> {
// try {
// return trustStore.getCertificate(t);
// } catch (KeyStoreException e2) {
// throw new RuntimeException("Error reading truststore", e2);
// }
// })
// .collect(Collectors.toList());
//
// X509Certificate[] certificates = certificateList.toArray(new X509Certificate[certificateList.size()]);
// //PrivateKey trustedCerts = (PrivateKey) keyStore.getKey("certs", "password".toCharArray());
// //PrivateKey privateKey = (PrivateKey) keyStore.getKey("certs", "password".toCharArray());
// //Certificate[] certChain = keyStore.getCertificateChain("certs");
//// X509Certificate[] x509CertificateChain = Arrays.stream(certChain)
//// .map(certificate -> (X509Certificate) certificate)
//// .collect(Collectors.toList())
//// .toArray(new X509Certificate[certChain.length]);
//
// SslContext sslContext = SslContextBuilder.forClient()
// .keyManager(keyManagerFactory)
// .trustManager(certificates)
// .build();
HttpClient httpClient = HttpClient.create()
.secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
return WebClient.builder()
.filter(serverOAuth2AuthorizedClientExchangeFilterFunction)
.clientConnector(connector)
.build();
当我使用 Webclient 获取访问令牌并使用该令牌启动服务调用时,我已经确认 SSL 证书设置正确并且可以正常工作。
当我使用内置的 oauth2 库时,出现以下错误:
org.springframework.security.oauth2.client.ClientAuthorizationException: [server_error]
我在堆栈跟踪中看到以下内容
reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to POST <url>[DefaultWebClient]
Stack trace:
java.lang.Exception: #block terminated with an error
我在这里做错了什么?
解决方案
除了按照原始帖子评论中的建议检查 SSL 证书外,请尝试在调试器中仔细检查服务器的响应。
我能够在org.springframework.security.oauth2.core.web.reactive.function.OAuth2AccessTokenResponseBodyExtractor::extract
(请参阅inputMessage.response.responseState.response.status
)中看到 HTTP 响应的状态代码
OAuth2AccessTokenResponseBodyExtractor::parse
通过在方法中设置断点,我能够看到 JSON 响应正文。
就我而言,问题很明显。代理返回了非 OAuth 错误消息。
为了在我的代码中捕获错误,我使用 WebClientReactiveClientCredentialsTokenResponseClient::setWebClient 和 WebClient.builder().filter() 使用此过滤器记录正文:
ExchangeFilterFunction logError = ExchangeFilterFunction.ofResponseProcessor(
clientResponse -> {
if (clientResponse.statusCode() != null && !clientResponse.statusCode().isError()) return Mono.just(clientResponse);
LOGGER.error("OAuth response error: {}", clientResponse.statusCode().toString());
return clientResponse
.bodyToMono(String.class)
.doOnNext(s -> {
LOGGER.error("OAuth response body: {}", s);
LOGGER.error("Note: OAuth response body has been consumed for logging. Spring OAuth client library will report that the response is empty.");
})
.map(s -> clientResponse);
});
推荐阅读
- java - JdbcTemplate queryForMap get() 为有效键返回 null
- java - 使用 RestTemplate 将 Rest Api 结果映射到对象
- javascript - 我正在按数字属性对数组进行排序,但现在我想使用字符串属性应用第二次排序。我可以吗?
- vue.js - 为什么我不能使用常量而不是 this.item.number?
- r - 关于回归模型中的绘图的错误消息
- php - 试图获取非对象的属性“头像” - Laravel 6
- c++ - 以线程安全的方式从 C/C++ 中的时区名称字符串转换时区偏移量
- powerbi - 如何计算每月的年度差异?
- r - 如何用数字 R 对列进行加权?
- php - 如何在 Laravel 中使用两个关系循环收集