mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2025-08-20 00:44:25 +08:00
🎨 #3655 通过支持现代 TLS 版本修复 SSL 握手失败问题
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
Some checks failed
Publish to Maven Central / build-and-publish (push) Has been cancelled
This commit is contained in:
parent
875c35e745
commit
14f8c8ebc2
@ -53,4 +53,10 @@ public interface ApacheHttpClientBuilder {
|
|||||||
* ssl连接socket工厂.
|
* ssl连接socket工厂.
|
||||||
*/
|
*/
|
||||||
ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory);
|
ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的TLS协议版本.
|
||||||
|
* Supported TLS protocol versions.
|
||||||
|
*/
|
||||||
|
ApacheHttpClientBuilder supportedProtocols(String[] supportedProtocols);
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,13 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApacheHttpClientBuilder supportedProtocols(String[] supportedProtocols) {
|
||||||
|
// This implementation doesn't use the supportedProtocols parameter as it relies on the provided SSLConnectionSocketFactory
|
||||||
|
// Users should configure the SSLConnectionSocketFactory with desired protocols before setting it
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取链接的超时时间设置,默认3000ms
|
* 获取链接的超时时间设置,默认3000ms
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -93,6 +93,12 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder {
|
|||||||
*/
|
*/
|
||||||
private String userAgent;
|
private String userAgent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的TLS协议版本,默认支持现代TLS版本
|
||||||
|
* Supported TLS protocol versions, defaults to modern TLS versions
|
||||||
|
*/
|
||||||
|
private String[] supportedProtocols = {"TLSv1.2", "TLSv1.3", "TLSv1.1", "TLSv1"};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义请求拦截器
|
* 自定义请求拦截器
|
||||||
*/
|
*/
|
||||||
@ -179,6 +185,12 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApacheHttpClientBuilder supportedProtocols(String[] supportedProtocols) {
|
||||||
|
this.supportedProtocols = supportedProtocols;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public IdleConnectionMonitorThread getIdleConnectionMonitorThread() {
|
public IdleConnectionMonitorThread getIdleConnectionMonitorThread() {
|
||||||
return this.idleConnectionMonitorThread;
|
return this.idleConnectionMonitorThread;
|
||||||
}
|
}
|
||||||
@ -257,7 +269,7 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder {
|
|||||||
|
|
||||||
return new SSLConnectionSocketFactory(
|
return new SSLConnectionSocketFactory(
|
||||||
sslcontext,
|
sslcontext,
|
||||||
new String[]{"TLSv1"},
|
this.supportedProtocols,
|
||||||
null,
|
null,
|
||||||
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
|
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
|
||||||
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
|
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
package me.chanjar.weixin.common.util.http.apache;
|
||||||
|
|
||||||
|
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.testng.Assert;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试SSL配置,特别是TLS协议版本配置
|
||||||
|
* Test SSL configuration, especially TLS protocol version configuration
|
||||||
|
*/
|
||||||
|
public class SSLConfigurationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultTLSProtocols() throws Exception {
|
||||||
|
// Create a new instance to check the default configuration
|
||||||
|
Class<?> builderClass = DefaultApacheHttpClientBuilder.class;
|
||||||
|
Object builder = builderClass.getDeclaredMethod("get").invoke(null);
|
||||||
|
|
||||||
|
// 验证默认支持的TLS协议版本包含现代版本
|
||||||
|
Field supportedProtocolsField = builderClass.getDeclaredField("supportedProtocols");
|
||||||
|
supportedProtocolsField.setAccessible(true);
|
||||||
|
String[] supportedProtocols = (String[]) supportedProtocolsField.get(builder);
|
||||||
|
|
||||||
|
List<String> protocolList = Arrays.asList(supportedProtocols);
|
||||||
|
|
||||||
|
System.out.println("Default supported TLS protocols: " + Arrays.toString(supportedProtocols));
|
||||||
|
|
||||||
|
// 主要验证:应该支持TLS 1.2和/或1.3 (现代安全版本)
|
||||||
|
// Main validation: Should support TLS 1.2 and/or 1.3 (modern secure versions)
|
||||||
|
Assert.assertTrue(protocolList.contains("TLSv1.2"), "Should support TLS 1.2");
|
||||||
|
Assert.assertTrue(protocolList.contains("TLSv1.3"), "Should support TLS 1.3");
|
||||||
|
|
||||||
|
// 验证不再是只有TLS 1.0 (这是导致原问题的根本原因)
|
||||||
|
// Verify it's no longer just TLS 1.0 (which was the root cause of the original issue)
|
||||||
|
Assert.assertTrue(protocolList.size() > 0, "Should support at least one TLS version");
|
||||||
|
boolean hasModernTLS = protocolList.contains("TLSv1.2") || protocolList.contains("TLSv1.3");
|
||||||
|
Assert.assertTrue(hasModernTLS, "Should support at least one modern TLS version (1.2 or 1.3)");
|
||||||
|
|
||||||
|
// 验证不是原来的老旧配置 (只有 "TLSv1")
|
||||||
|
// Verify it's not the old configuration (only "TLSv1")
|
||||||
|
boolean isOldConfig = protocolList.size() == 1 && protocolList.contains("TLSv1");
|
||||||
|
Assert.assertFalse(isOldConfig, "Should not be the old configuration that only supported TLS 1.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomTLSProtocols() throws Exception {
|
||||||
|
// Test that we can set custom TLS protocols
|
||||||
|
String[] customProtocols = {"TLSv1.2", "TLSv1.3"};
|
||||||
|
|
||||||
|
// Create a new builder instance using reflection to avoid singleton issues in testing
|
||||||
|
Class<?> builderClass = DefaultApacheHttpClientBuilder.class;
|
||||||
|
Constructor<?> constructor = builderClass.getDeclaredConstructor();
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
Object builder = constructor.newInstance();
|
||||||
|
|
||||||
|
// Set custom protocols
|
||||||
|
builderClass.getMethod("supportedProtocols", String[].class).invoke(builder, (Object) customProtocols);
|
||||||
|
|
||||||
|
Field supportedProtocolsField = builderClass.getDeclaredField("supportedProtocols");
|
||||||
|
supportedProtocolsField.setAccessible(true);
|
||||||
|
String[] actualProtocols = (String[]) supportedProtocolsField.get(builder);
|
||||||
|
|
||||||
|
Assert.assertEquals(actualProtocols, customProtocols, "Custom protocols should be set correctly");
|
||||||
|
|
||||||
|
System.out.println("Custom supported TLS protocols: " + Arrays.toString(actualProtocols));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSSLContextCreation() throws Exception {
|
||||||
|
DefaultApacheHttpClientBuilder builder = DefaultApacheHttpClientBuilder.get();
|
||||||
|
|
||||||
|
// 构建HTTP客户端以验证SSL工厂是否正确创建
|
||||||
|
CloseableHttpClient client = builder.build();
|
||||||
|
Assert.assertNotNull(client, "HTTP client should be created successfully");
|
||||||
|
|
||||||
|
// 验证SSL上下文支持现代TLS协议
|
||||||
|
SSLContext sslContext = SSLContext.getDefault();
|
||||||
|
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
|
||||||
|
|
||||||
|
// 创建一个SSL socket来检查支持的协议
|
||||||
|
try (SSLSocket socket = (SSLSocket) socketFactory.createSocket()) {
|
||||||
|
String[] supportedProtocols = socket.getSupportedProtocols();
|
||||||
|
List<String> supportedList = Arrays.asList(supportedProtocols);
|
||||||
|
|
||||||
|
// JVM应该支持TLS 1.2(在JDK 8+中默认可用)
|
||||||
|
Assert.assertTrue(supportedList.contains("TLSv1.2"),
|
||||||
|
"JVM should support TLS 1.2. Supported protocols: " + Arrays.toString(supportedProtocols));
|
||||||
|
|
||||||
|
System.out.println("JVM supported TLS protocols: " + Arrays.toString(supportedProtocols));
|
||||||
|
}
|
||||||
|
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuilderChaining() {
|
||||||
|
DefaultApacheHttpClientBuilder builder = DefaultApacheHttpClientBuilder.get();
|
||||||
|
|
||||||
|
// 测试方法链调用
|
||||||
|
ApacheHttpClientBuilder result = builder
|
||||||
|
.supportedProtocols(new String[]{"TLSv1.2", "TLSv1.3"})
|
||||||
|
.httpProxyHost("proxy.example.com")
|
||||||
|
.httpProxyPort(8080);
|
||||||
|
|
||||||
|
Assert.assertSame(result, builder, "Builder methods should return the same instance for method chaining");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package me.chanjar.weixin.common.util.http.apache;
|
||||||
|
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.testng.Assert;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 集成测试 - 验证SSL配置可以正常访问HTTPS网站
|
||||||
|
* Integration test - Verify SSL configuration can access HTTPS websites properly
|
||||||
|
*/
|
||||||
|
public class SSLIntegrationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHTTPSConnectionWithModernTLS() throws Exception {
|
||||||
|
DefaultApacheHttpClientBuilder builder = DefaultApacheHttpClientBuilder.get();
|
||||||
|
|
||||||
|
// 使用默认配置(支持现代TLS版本)创建客户端
|
||||||
|
CloseableHttpClient client = builder.build();
|
||||||
|
|
||||||
|
// 测试访问一个需要现代TLS的网站
|
||||||
|
// Test accessing a website that requires modern TLS
|
||||||
|
HttpGet httpGet = new HttpGet("https://api.weixin.qq.com/");
|
||||||
|
|
||||||
|
try (CloseableHttpResponse response = client.execute(httpGet)) {
|
||||||
|
// 验证能够成功建立HTTPS连接(不管响应内容是什么)
|
||||||
|
// Verify that HTTPS connection can be established successfully (regardless of response content)
|
||||||
|
Assert.assertNotNull(response, "Should be able to establish HTTPS connection");
|
||||||
|
Assert.assertNotNull(response.getStatusLine(), "Should receive a status response");
|
||||||
|
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
// 任何HTTP状态码都表示SSL握手成功
|
||||||
|
// Any HTTP status code indicates successful SSL handshake
|
||||||
|
Assert.assertTrue(statusCode > 0, "Should receive a valid HTTP status code, got: " + statusCode);
|
||||||
|
|
||||||
|
System.out.println("HTTPS connection test successful. Status: " + response.getStatusLine());
|
||||||
|
} catch (javax.net.ssl.SSLHandshakeException e) {
|
||||||
|
Assert.fail("SSL handshake should not fail with modern TLS configuration. Error: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomTLSConfiguration() throws Exception {
|
||||||
|
DefaultApacheHttpClientBuilder builder = DefaultApacheHttpClientBuilder.get();
|
||||||
|
|
||||||
|
// 配置为只支持TLS 1.2和1.3(最安全的配置)
|
||||||
|
// Configure to only support TLS 1.2 and 1.3 (most secure configuration)
|
||||||
|
builder.supportedProtocols(new String[]{"TLSv1.2", "TLSv1.3"});
|
||||||
|
|
||||||
|
CloseableHttpClient client = builder.build();
|
||||||
|
|
||||||
|
// 测试这个配置是否能正常工作
|
||||||
|
HttpGet httpGet = new HttpGet("https://httpbin.org/get");
|
||||||
|
|
||||||
|
try (CloseableHttpResponse response = client.execute(httpGet)) {
|
||||||
|
Assert.assertNotNull(response, "Should be able to establish HTTPS connection with TLS 1.2/1.3");
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
Assert.assertEquals(statusCode, 200, "Should get HTTP 200 response from httpbin.org");
|
||||||
|
|
||||||
|
System.out.println("Custom TLS configuration test successful. Status: " + response.getStatusLine());
|
||||||
|
} catch (javax.net.ssl.SSLHandshakeException e) {
|
||||||
|
// 这个测试可能会因为网络环境而失败,所以我们只是记录警告
|
||||||
|
// This test might fail due to network environment, so we just log a warning
|
||||||
|
System.out.println("Warning: SSL handshake failed with custom TLS config: " + e.getMessage());
|
||||||
|
System.out.println("This might be due to network restrictions in the test environment.");
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user