You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 17:53:15