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工厂.
|
||||
*/
|
||||
ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory);
|
||||
|
||||
/**
|
||||
* 支持的TLS协议版本.
|
||||
* Supported TLS protocol versions.
|
||||
*/
|
||||
ApacheHttpClientBuilder supportedProtocols(String[] supportedProtocols);
|
||||
}
|
||||
|
@ -117,6 +117,13 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder {
|
||||
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
|
||||
* <p>
|
||||
|
@ -93,6 +93,12 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder {
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApacheHttpClientBuilder supportedProtocols(String[] supportedProtocols) {
|
||||
this.supportedProtocols = supportedProtocols;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdleConnectionMonitorThread getIdleConnectionMonitorThread() {
|
||||
return this.idleConnectionMonitorThread;
|
||||
}
|
||||
@ -257,7 +269,7 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder {
|
||||
|
||||
return new SSLConnectionSocketFactory(
|
||||
sslcontext,
|
||||
new String[]{"TLSv1"},
|
||||
this.supportedProtocols,
|
||||
null,
|
||||
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
|
||||
} 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