fix ssl bug

This commit is contained in:
Looly 2024-12-19 14:00:10 +08:00
parent f3d63f85c3
commit f76519351d
3 changed files with 163 additions and 27 deletions

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.http.client.engine.httpclient4;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.dromara.hutool.core.lang.builder.Builder;
import org.dromara.hutool.http.ssl.SSLInfo;
/**
* HttpClient4连接工厂注册器构建器
*
* @author Looly
* @since 6.0.0
*/
public class ConnectionSocketFactoryRegistryBuilder implements Builder<Registry<ConnectionSocketFactory>> {
private static final long serialVersionUID = 1L;
/**
* 创建
*
* @return {@code ConnectionSocketFactoryRegistryBuilder}
*/
public static ConnectionSocketFactoryRegistryBuilder of() {
return new ConnectionSocketFactoryRegistryBuilder();
}
/**
* 构建默认的连接工厂注册器默认支持SSL
*
* @param sslInfo SSL配置{@code null}表示使用默认配置{@link SSLConnectionSocketFactory#getSocketFactory()}
* @return {@code Registry<ConnectionSocketFactory>}
*/
public static Registry<ConnectionSocketFactory> build(final SSLInfo sslInfo) {
return of().registerPlainHttp().registerHttps(sslInfo).build();
}
private final RegistryBuilder<ConnectionSocketFactory> builder;
/**
* 构造
*/
public ConnectionSocketFactoryRegistryBuilder() {
builder = RegistryBuilder.create();
}
/**
* 注册HTTP协议使用默认的普通非加密连接工厂
*
* @return this
*/
public ConnectionSocketFactoryRegistryBuilder registerPlainHttp() {
return registerHttp(PlainConnectionSocketFactory.getSocketFactory());
}
/**
* 注册HTTP协议使用默认的普通连接工厂
*
* @param socketFactory {@link ConnectionSocketFactory}
* @return this
*/
public ConnectionSocketFactoryRegistryBuilder registerHttp(final ConnectionSocketFactory socketFactory) {
return register("http", socketFactory);
}
/**
* 注册HTTPS协议
*
* @param sslInfo SSL配置{@code null}表示使用默认配置{@link SSLConnectionSocketFactory#getSocketFactory()}
* @return this
*/
public ConnectionSocketFactoryRegistryBuilder registerHttps(final SSLInfo sslInfo) {
return register("https", buildSocketFactory(sslInfo));
}
/**
* 注册连接工厂
*
* @param protocol 协议
* @param socketFactory 连接工厂
* @return this
*/
public ConnectionSocketFactoryRegistryBuilder register(final String protocol, final ConnectionSocketFactory socketFactory) {
builder.register(protocol, socketFactory);
return this;
}
@Override
public Registry<ConnectionSocketFactory> build() {
return builder.build();
}
/**
* 支持SSL
*
* @param sslInfo SSL配置{@code null}表示使用默认配置{@link SSLConnectionSocketFactory#getSocketFactory()}
* @return SSLConnectionSocketFactory
*/
private static SSLConnectionSocketFactory buildSocketFactory(final SSLInfo sslInfo) {
if (null == sslInfo) {
return SSLConnectionSocketFactory.getSocketFactory();
}
return new SSLConnectionSocketFactory(
sslInfo.getSslContext(),
sslInfo.getProtocols(),
null,
sslInfo.getHostnameVerifier());
}
}

View File

@ -23,7 +23,6 @@ import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
@ -43,7 +42,6 @@ import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.http.client.cookie.InMemoryCookieStore;
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
import org.dromara.hutool.http.proxy.ProxyInfo;
import org.dromara.hutool.http.ssl.SSLInfo;
import java.io.IOException;
import java.net.PasswordAuthentication;
@ -112,13 +110,7 @@ public class HttpClient4Engine extends AbstractClientEngine {
final HttpClientBuilder clientBuilder = HttpClients.custom();
final ClientConfig config = ObjUtil.defaultIfNull(this.config, ApacheHttpClientConfig::of);
// SSL配置
final SSLInfo sslInfo = config.getSslInfo();
if (null != sslInfo) {
clientBuilder.setSSLSocketFactory(buildSocketFactory(sslInfo));
}
// 连接配置包括连接池
// 连接配置包括SSL配置和连接池
clientBuilder.setConnectionManager(buildConnectionManager(config));
// 实例级别默认请求配置
@ -136,7 +128,7 @@ public class HttpClient4Engine extends AbstractClientEngine {
setProxy(clientBuilder, config);
// Cookie管理
if(config.isUseCookieManager()){
if (config.isUseCookieManager()) {
this.cookieStore = new InMemoryCookieStore();
clientBuilder.setDefaultCookieStore(new HttpClient4CookieStore(this.cookieStore));
}
@ -155,19 +147,6 @@ public class HttpClient4Engine extends AbstractClientEngine {
return result;
}
/**
* 支持SSL
*
* @return SSLConnectionSocketFactory
*/
private static SSLConnectionSocketFactory buildSocketFactory(final SSLInfo sslInfo) {
return new SSLConnectionSocketFactory(
sslInfo.getSslContext(),
sslInfo.getProtocols(),
null,
sslInfo.getHostnameVerifier());
}
/**
* 构建连接池管理器
*
@ -175,17 +154,20 @@ public class HttpClient4Engine extends AbstractClientEngine {
* @return PoolingHttpClientConnectionManager
*/
private PoolingHttpClientConnectionManager buildConnectionManager(final ClientConfig config) {
final PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
final PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(
// SSL配置当config.getSslInfo()为null时使用默认配置
ConnectionSocketFactoryRegistryBuilder.build(config.getSslInfo())
);
// 连接池配置
if (config instanceof ApacheHttpClientConfig) {
final ApacheHttpClientConfig apacheHttpClientConfig = (ApacheHttpClientConfig) config;
final int maxTotal = apacheHttpClientConfig.getMaxTotal();
if(maxTotal > 0){
if (maxTotal > 0) {
manager.setMaxTotal(maxTotal);
}
final int maxPerRoute = apacheHttpClientConfig.getMaxPerRoute();
if(maxPerRoute > 0){
if (maxPerRoute > 0) {
manager.setDefaultMaxPerRoute(maxPerRoute);
}
}
@ -234,7 +216,7 @@ public class HttpClient4Engine extends AbstractClientEngine {
final ProxyInfo proxy = config.getProxy();
if (null != proxy) {
final ProxySelector proxySelector = proxy.getProxySelector();
if(null != proxySelector){
if (null != proxySelector) {
clientBuilder.setRoutePlanner(new SystemDefaultRoutePlanner(proxySelector));
}
final PasswordAuthentication auth = proxy.getAuth();

View File

@ -0,0 +1,28 @@
package org.dromara.hutool.http.client;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.http.HttpGlobalConfig;
import org.dromara.hutool.http.HttpUtil;
import org.dromara.hutool.http.client.engine.ClientEngine;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
public class IssueIBC5N8Test {
@Test
@Disabled
public void getBadSSLTest(){
HttpGlobalConfig.setTrustAnyHost(true);
requestBadSSL("httpclient4");
requestBadSSL("httpclient5");
requestBadSSL("okhttp");
requestBadSSL("jdkClient");
}
private void requestBadSSL(final String engineName) {
final ClientEngine engine = HttpUtil.createClient(engineName);
final Request req = Request.of("https://expired.badssl.com/");
final Response res = engine.send(req);
Console.log(res.getStatus());
}
}