问题描述
有时需要允许不安全的HTTPS连接,例如在一些应该与任何站点一起使用的Web爬行应用程序中。我使用旧的HttpsURLConnection API最近被JDK中的取代11.使用这个新API允许不安全的HTTPS连接(自签名或过期证书)的方法是什么?
Sometimes it is needed to allow insecure HTTPS connections, e.g. in some web-crawling applications which should work with any site. I used one such solution with old HttpsURLConnection API which was recently superseded by the new HttpClient API in JDK 11. What is the way to allow insecure HTTPS connections (self-signed or expired certificate) with this new API?
UPD:我尝试的代码(在Kotlin但是地图直接到Java):
UPD: The code I tried (in Kotlin but maps directly to Java):
val trustAllCerts = arrayOf<TrustManager>(object: X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate>? = null
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {}
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {}
})
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
val sslParams = SSLParameters()
// This should prevent host validation
sslParams.endpointIdentificationAlgorithm = ""
httpClient = HttpClient.newBuilder()
.sslContext(sslContext)
.sslParameters(sslParams)
.build()
但在发送时我有异常(尝试使用自签名证书的localhost):
But on sending I have exception (trying on localhost with self-signed certificate):
java.io.IOException: No name matching localhost found
使用IP地址而不是localhost给出没有主题替代名称存在异常。
Using IP address instead of localhost gives "No subject alternative names present" exception.
经过一些JDK调试后我发现 sslParams
在抛出异常的地方被忽略,并且使用了一些本地创建的实例。进一步调试显示,影响主机名验证算法的唯一方法是将 jdk.internal.httpclient.disableHostnameVerification
系统属性设置为true。这似乎是一个解决方案。上面代码中的 SSLParameters
无效,因此可以丢弃此部分。使其仅在全局配置看起来像是新的HttpClient API中的严重设计缺陷。
After some debugging of JDK I found that sslParams
are really ignored in the place the exception is thrown and some locally created instance is used. Further debugging revealed that the only way to affect the hostname verification algorithm is setting jdk.internal.httpclient.disableHostnameVerification
system property to true. And that's seems to be a solution. SSLParameters
in the code above have no effect so this part can be discarded. Making it configurable only globally looks like serious design flaw in new HttpClient API.
推荐答案
使用Java 11,你也可以做一个类似的工作与 HttpClient
内置为:
With Java 11, as well you can do a similar effort as mentioned in the selected answer in the link shared with the HttpClient
built as:
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofMillis(<timeoutInSeconds> * 1000))
.sslContext(sc) // SSL context 'sc' initialised as earlier
.sslParameters(parameters) // ssl parameters if overriden
.build();
带样品请求
HttpRequest requestBuilder = HttpRequest.newBuilder()
.uri(URI.create("https://www.example.com/getSomething"))
.GET()
.build();
可以执行:
httpClient.send(requestBuilder, HttpResponse.BodyHandlers.ofString()); // sends the request
从评论更新到禁用主机名验证,目前可以使用系统属性:
Update from comments, to disable the hostname verification, currently one can use the system property:
-Djdk.internal.httpclient.disableHostnameVerification
可以如下: -
final Properties props = System.getProperties();
props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
这篇关于允许Java JDK 11 HttpClient的不安全的HTTPS连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!