最近在使用JDK11及以后支持的Httpclient,需求是需要证书双向认证,但是要忽略域名的校验,以前用apache httpclient的时候有一个DefaultHostnameVerifier
可以会忽略域名校验,但是jdk自带的httpclient并没有支持此功能。
一句话解决方案:
代码配置
final Properties props = System.getProperties();
props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
或者启动参数配置
-Djdk.internal.httpclient.disableHostnameVerification
下面是源码探究。
在使用httpclient时,会发现它支持配置SslParams, SSLParameters
是SSLEngine使用的,如果SSLParameters
的identificationAlgorithm
属性为空,SSLEngine就会跳过校验域名,代码在:
sun.security.ssl.X509TrustManagerImpl
的288行,代码如下:
// check endpoint identity
String identityAlg = engine.getSSLParameters().
getEndpointIdentificationAlgorithm();
// 发现为空就跳过域名的校验
if (identityAlg != null && !identityAlg.isEmpty()) {
checkIdentity(session, trustedChain,
identityAlg, checkClientTrusted);
}
那么我们是不是就可以配置httpclient的SSLParameters
,直接达成目标呢?
SSLParameters parameters = new SSLParameters();
parameters.setEndpointIdentificationAlgorithm("");
var httpClient = HttpClient.newBuilder()
...
.sslParameters(parameters)
.build();
看起来我们是配置成功了,但是实际使用时仍然会发现该配置无效,原因是这个配置项被Httpclient丢弃了,在jdk.internal.net.http.AbstractAsyncSSLConnection
的createSSLParameters(HttpClientImpl client, ServerName serverName,String[] alpn)
方法,可以看到用于创建SSLEngine的SSLParameters
被创建的过程:
private static SSLParameters createSSLParameters(HttpClientImpl client,
ServerName serverName,
String[] alpn) {
SSLParameters sslp = client.sslParameters();
// 复制了一份 SSLParameters
SSLParameters sslParameters = Utils.copySSLParameters(sslp);
// 其他配置内容
// 重点: disableHostnameVerification 这个变量会导致我们配置的IdentificationAlgorithm被覆盖!
if (!disableHostnameVerification)
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
return sslParameters;
}
在代码中可以发现,AbstractAsyncSSLConnection的disableHostnameVerification
属性会导致我们配置的sslParams.identificationAlgorithm
被覆盖,从而导致配置无效。
继续跟下去,disableHostnameVerification
是jdk.internal.net.http.common.Utils.isHostnameVerificationDisabled
属性,而类 jdk.internal.net.http.common.Utils
是httpclient的内部类,未对外开放,所以我们无法在外部修改。通过阅读java.net.http
的module-info.java
,里面有说明可以修改此变量:
jdk.internal.httpclient.disableHostnameVerification (default: false)
If true (or set to an empty string), hostname verification in SSL certificates is disabled. This is a system property only and not available in conf/ net. properties. It is provided for testing purposes only.
由此,真相大白,这项配置必须在system property中配置。