CloseableHttpAsyncClient关闭连接前未发送致命TLS警报的配置咨询
让Apache HttpClient5异步客户端发送TLS致命警报的配置方案
Apache HttpClient5的CloseableHttpAsyncClient在SSL握手失败(如证书过期)时,默认不会发送TLS致命警报,而同步的CloseableHttpClient会遵循TLS协议要求发送警报。核心原因是两者连接关闭策略不同:同步客户端默认采用优雅关闭逻辑,允许JSSE完成警报发送;而异步客户端默认可能直接强制关闭连接,跳过了警报发送流程。
以下是两种可行的配置方案:
方案一:启用优雅关闭连接
通过配置ConnectionConfig的closeGracefully参数为true,让异步客户端在连接关闭时优先发送TLS警报再断开连接。
代码示例:
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.config.ConnectionConfig; import java.net.URI; import java.util.concurrent.Future; public class AsyncClientTlsAlertExample { public static void main(String[] args) { // 配置连接启用优雅关闭 ConnectionConfig connectionConfig = ConnectionConfig.custom() .setCloseGracefully(true) .build(); // 构建带自定义连接配置的连接管理器 PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create() .setDefaultConnectionConfig(connectionConfig) .build(); try (CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setConnectionManager(connectionManager) .build()) { client.start(); SimpleHttpRequest request = SimpleHttpRequest.create(Method.GET, URI.create("https://expired.badssl.com")); Future<SimpleHttpResponse> future = client.execute(request, null); try { future.get(); } catch (Exception e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } } }
方案二:自定义SSL连接套接字工厂
如果方案一不生效,可以自定义SSLConnectionSocketFactory,在SSL握手失败时显式关闭SSLSocket,触发JSSE发送TLS警报。
代码示例:
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.HttpContext; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSocket; import java.io.IOException; import java.net.InetSocketAddress; import java.net.URI; import java.util.concurrent.Future; public class CustomSslAsyncClientExample { public static void main(String[] args) throws Exception { SSLContext sslContext = SSLContext.getDefault(); // 自定义SSL连接套接字工厂,处理握手异常时显式关闭socket SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier()) { @Override public void connectSocket(int connectTimeout, java.net.Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException { try { super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context); } catch (SSLHandshakeException e) { // 握手失败时,显式关闭SSLSocket触发警报发送 if (socket instanceof SSLSocket sslSocket) { sslSocket.close(); } throw e; } } }; PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create() .setSSLSocketFactory(sslSocketFactory) .build(); try (CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setConnectionManager(connectionManager) .build()) { client.start(); SimpleHttpRequest request = SimpleHttpRequest.create(Method.GET, URI.create("https://expired.badssl.com")); Future<SimpleHttpResponse> future = client.execute(request, null); try { future.get(); } catch (Exception e) { e.printStackTrace(); } } } }
验证
配置完成后,通过Wireshark抓包或服务器日志可以确认,异步客户端在证书验证失败时会发送TLS致命警报(如certificate_expired)后再关闭连接,符合TLS v1.2协议要求。
内容的提问来源于stack exchange,提问作者rubenv
相关产品推荐
相关产品推荐

