spring-boot - Oauth2RestTemplate - 不从自定义信任库中读取证书
问题描述
默认情况下,Oauth2RestTemplate 从 jre 信任库读取证书。但是当我们使用 HttpComponentsClientHttpRequestFactory 配置模板时,它不会从自定义信任库中读取证书。下面是代码:
@Configuration
public class OauthConfig {
@Resource
private HttpComponentsClientHttpRequestFactory backendHttpComponentsClientHttpRequestFactory;
@Resource
private PoolingHttpClientConnectionManager backendPoolingHttpClientConnectionManager;
@Value("${keystore.path}")
private File certificatePath;
@Value("${keystore.password}")
private String certificatePassword;
@Bean("testOauthRestTemplate")
public OAuth2RestOperations oauth2RestTemplate() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException
{
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
resourceDetails.setClientId("clientId");
resourceDetails.setClientSecret("12345");
resourceDetails.setAccessTokenUri("https://test:8080/oauth/token");
resourceDetails.setGrantType("client_credentials");
resourceDetails.setAuthenticationScheme(AuthenticationScheme.header);
AccessTokenRequest request = new DefaultAccessTokenRequest();
OAuth2RestTemplate oauth2RestTemplate = new OAuth2RestTemplate(resourceDetails, new DefaultOAuth2ClientContext(request));
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
HttpClient httpClient = null;
httpClient = clientBuilder.setConnectionManager(backendPoolingHttpClientConnectionManager)
.setSSLSocketFactory(SslSettings.sslConnectionSocketFactory(certificatePath, certificatePassword))
.disableCookieManagement().build();
backendHttpComponentsClientHttpRequestFactory.setHttpClient(httpClient);
oauth2RestTemplate.setRequestFactory(backendHttpComponentsClientHttpRequestFactory);
return oauth2RestTemplate;
}
@Bean("backendHttpComponentsClientHttpRequestFactory")
public HttpComponentsClientHttpRequestFactory backendHttpComponentsClientHttpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory();
}
@Bean("backendPoolingHttpClientConnectionManager")
public PoolingHttpClientConnectionManager backendPoolingHttpClientConnectionManager() throws KeyManagementException,
NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", SslSettings.sslConnectionSocketFactory(certificatePath, certificatePassword))
.build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
new PoolingHttpClientConnectionManager(
socketFactoryRegistry);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20);
poolingHttpClientConnectionManager.setMaxTotal(200);
return poolingHttpClientConnectionManager;
}
}
以下是例外情况:
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1964)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:328)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:322)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1614)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1052)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:987)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1072)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:162)
at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:76)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:723)
... 73 common frames omitted
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1596)
... 87 common frames omitted
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
... 93 common frames omitted
需要一些建议/帮助..无法找到解决方案..
解决方案
当前接受的答案给出了一个推理不正确的解决方案。该错误不是因为您的 OAuth2RestTemplate 无法访问证书存储,而是因为 OAuth2RestTemplate 上的 AccessTokenProvider 在内部创建了自己的 RestTemplate 以请求令牌。为了为该内部 RestTemplate 设置提供程序,您可以执行以下操作(根据您正在执行的 OAuth 类型更改为不同类型的 AccessTokenProviders)
ClientCredentialsAccessTokenProvider provider = new ClientCredentialsAccessTokenProvider();
provider.setRequestFactory(requestFactory);
restTemplate.setAccessTokenProvider(provider);
接受的答案通过将 trustStore 应用于所有请求来工作,这并不总是您想要做的,因为它几乎总是更具体。
推荐阅读
- java - Kubernetes for MySQL 中的 Spring DataSource
- ios - 如果标签在 Xcode (Swift) 中被截断,如何更改 UILabel 大小
- laravel - 我想从 reslource/json 路径获取所有 json 文件并对其进行版本控制,但我不知道我必须使用哪个函数
- arrays - 为什么这两种方法会产生不同的结果?
- python - 如何在调用另一个类方法时执行一个类方法
- php - 用简单的 HTML Dom 解析 SVG
- grpc - 最大 gRPC 连接数基准
- c# - 如何获取路由属性 dotnet core 3?
- numpy - Pytorch:将 [1,x] 大小的张量转换为 [x] 大小的张量
- xamarin - ActivityProvider 不运行