如何忽略 Feign 中的 SSL 证书信任错误?
How to ignore SSL cert trust errors in Feign?
如何在 feign
客户端中实现 curl -k
?
我知道我能做到。只是想知道有没有办法忽略或禁用。
new Client.Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier)
免责声明
您实际上不应该这样做,原因有很多。解决 SSL 问题的最简单方法是实际遵循 SSL 最佳实践并使用有效证书。有一些优秀的在线项目,例如 https://letsencrypt.org/ 如果主机是可公开访问的(如果它有一个可以验证的真实主机名),它们甚至可以让您免费获得很好的安全性。
使用风险自负。确保您了解您正在违反许多最佳实践,并且只有在您了解这一点时才使用它。
如果您使用此示例代码造成某种类型的重大问题,您需要承担责任。
真话
我在处理我想从 spring-boot 应用程序调用的内部(公共不可访问)服务时遇到了同样的问题,我使用以下代码解决了它。
简要概述
很多人会告诉您,您可以接受所有证书,在其中硬编码您的特定证书,或者其他方式。您实际上可以只允许某些受信任的主机通过代码路径,这就是我在这里尝试的额外安全层。
在此示例代码中,您可以将多个主机传递给 类 并且它应该只允许向那些主机发出请求并发出无效证书,其他所有内容都将通过正常的命令链进行。
这不是真正的生产级代码,但希望您能从中得到一些用处。
讲完了,下面的内容可能是你最感兴趣的。
代码
这用于 Java 8 和 spring-boot。
配置
@Configuration
public class FeignClientConfiguration {
@Bean
public Client client() throws NoSuchAlgorithmException,
KeyManagementException {
return new Client.Default(
new NaiveSSLSocketFactory("your.host.here"),
new NaiveHostnameVerifier("your.host.here"));
}
}
NaiveHostnameVerifier
public class NaiveHostnameVerifier implements HostnameVerifier {
private final Set<String> naivelyTrustedHostnames;
private final HostnameVerifier hostnameVerifier =
HttpsURLConnection.getDefaultHostnameVerifier();
public NaiveHostnameVerifier(String ... naivelyTrustedHostnames) {
this.naivelyTrustedHostnames =
Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(naivelyTrustedHostnames)));
}
@Override
public boolean verify(String hostname, SSLSession session) {
return naivelyTrustedHostnames.contains(hostname) ||
hostnameVerifier.verify(hostname, session);
}
}
NaiveSSLSocketFactory
public class NaiveSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory sslSocketFactory =
(SSLSocketFactory) SSLSocketFactory.getDefault();
private final SSLContext alwaysAllowSslContext;
private final Set<String> naivelyTrustedHostnames;
public NaiveSSLSocketFactory(String ... naivelyTrustedHostnames)
throws NoSuchAlgorithmException, KeyManagementException {
this.naivelyTrustedHostnames =
Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(naivelyTrustedHostnames)));
alwaysAllowSslContext = SSLContext.getInstance("TLS");
TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
alwaysAllowSslContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public String[] getDefaultCipherSuites() {
return sslSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return (naivelyTrustedHostnames.contains(host))
? alwaysAllowSslContext.getSocketFactory().createSocket(socket, host, port, autoClose)
: sslSocketFactory.createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return (naivelyTrustedHostnames.contains(host))
? alwaysAllowSslContext.getSocketFactory().createSocket(host, port)
: sslSocketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException, UnknownHostException {
return (naivelyTrustedHostnames.contains(host))
? alwaysAllowSslContext.getSocketFactory().createSocket(host, port, localAddress, localPort)
: sslSocketFactory.createSocket(host, port, localAddress, localPort);
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return (naivelyTrustedHostnames.contains(host.getHostName()))
? alwaysAllowSslContext.getSocketFactory().createSocket(host, port)
: sslSocketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
return (naivelyTrustedHostnames.contains(host.getHostName()))
? alwaysAllowSslContext.getSocketFactory().createSocket(host, port, localHost, localPort)
: sslSocketFactory.createSocket(host, port, localHost, localPort);
}
}
参考资料
我大量借鉴了这个答案:
使用 Spring Cloud Netflix >= 1.4 时。4.RELEASE 您还可以执行以下操作:
添加okhttp客户端maven依赖:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
并添加以下属性:
feign.httpclient.disableSslValidation=true
feign.httpclient.enabled=false
feign.okhttp.enabled=true
参考:
https://github.com/spring-cloud/spring-cloud-netflix/issues/2729
通过在 application.yaml 中添加以下 属性 来禁用 ssl 验证。
feign.httpclient.disableSslValidation=真
或作为 VM 参数
-Dfeign.httpclient.disableSslValidation=true
feign.httpclient.disableSslValidation = true 对我不起作用。
通过以下代码在配置中创建客户端 bean 有效:
import feign.Client;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.ssl.SSLContexts;
import org.springframework.context.annotation.Bean;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
public class ClientConfiguration {
@Bean
public Client feignClient() {
return new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
}
private SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
return sslContext.getSocketFactory();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
pom.xml 可能需要添加依赖项:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
通过假配置覆盖
@Bean
public Client feignClient()
{
Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
return trustSSLSockets;
}
private SSLSocketFactory getSSLSocketFactory() {
try {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
return sslContext.getSocketFactory();
} catch (Exception exception) {
}
return null;
}
将以下 class 添加到您的存储库并将此 class 用作配置
这段代码对我有用:
@Configuration
public class SSLSocketClient {
@Bean
public Client feignClient() {
return new Client.Default(getSSLSocketFactory(),getHostnameVerifier());
}
//SSLSocketFactory
// Install the all-trusting trust manager
public static SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, getTrustManager(), new SecureRandom());
return sslContext.getSocketFactory();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
//TrustManager
// trust manager that does not validate certificate chains
private static TrustManager[] getTrustManager() {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[]{};
}
}};
return trustAllCerts;
}
//HostnameVerifier
public static HostnameVerifier getHostnameVerifier() {
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession)
{
return true;
}
};
return hostnameVerifier;
}}
当前版本的 spring-cloud-starter-openfeign 抑制主机名验证的工作方式如下。
使用 apache httpclient 时:
在 application.yml 中设置禁用 ssl 验证 属性
feign.httpclient.disable-ssl-validation: true
在pom.xml中添加feign-httpclient依赖。
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
如果您更喜欢 okhttp,则必须使用另一个应用程序启用 okhttp 属性 并添加 feign-okhttp 依赖项:
feign.httpclient.disableSslValidation=true
feign.httpclient.enabled=false
feign.okhttp.enabled=true
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
对于 httpclient5 (hc5),属性 disable-ssl-validation 遗憾的是没有关闭主机名验证(还没有?),这是票证:https://github.com/spring-cloud/spring-cloud-openfeign/issues/625
启用 hc5 的应用程序属性。
feign.httpclient.disableSslValidation=true
feign.httpclient.hc5.enabled=true
要添加的 Maven 依赖项
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hc5</artifactId>
</dependency>
注意: 对我来说棘手的部分是我错过了添加 feign-httpclient 作为依赖项。在这种情况下,使用启用了主机名验证的默认假客户端。
如何在 feign
客户端中实现 curl -k
?
我知道我能做到。只是想知道有没有办法忽略或禁用。
new Client.Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier)
免责声明
您实际上不应该这样做,原因有很多。解决 SSL 问题的最简单方法是实际遵循 SSL 最佳实践并使用有效证书。有一些优秀的在线项目,例如 https://letsencrypt.org/ 如果主机是可公开访问的(如果它有一个可以验证的真实主机名),它们甚至可以让您免费获得很好的安全性。
使用风险自负。确保您了解您正在违反许多最佳实践,并且只有在您了解这一点时才使用它。
如果您使用此示例代码造成某种类型的重大问题,您需要承担责任。
真话
我在处理我想从 spring-boot 应用程序调用的内部(公共不可访问)服务时遇到了同样的问题,我使用以下代码解决了它。
简要概述
很多人会告诉您,您可以接受所有证书,在其中硬编码您的特定证书,或者其他方式。您实际上可以只允许某些受信任的主机通过代码路径,这就是我在这里尝试的额外安全层。
在此示例代码中,您可以将多个主机传递给 类 并且它应该只允许向那些主机发出请求并发出无效证书,其他所有内容都将通过正常的命令链进行。
这不是真正的生产级代码,但希望您能从中得到一些用处。
讲完了,下面的内容可能是你最感兴趣的。
代码
这用于 Java 8 和 spring-boot。
配置
@Configuration
public class FeignClientConfiguration {
@Bean
public Client client() throws NoSuchAlgorithmException,
KeyManagementException {
return new Client.Default(
new NaiveSSLSocketFactory("your.host.here"),
new NaiveHostnameVerifier("your.host.here"));
}
}
NaiveHostnameVerifier
public class NaiveHostnameVerifier implements HostnameVerifier {
private final Set<String> naivelyTrustedHostnames;
private final HostnameVerifier hostnameVerifier =
HttpsURLConnection.getDefaultHostnameVerifier();
public NaiveHostnameVerifier(String ... naivelyTrustedHostnames) {
this.naivelyTrustedHostnames =
Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(naivelyTrustedHostnames)));
}
@Override
public boolean verify(String hostname, SSLSession session) {
return naivelyTrustedHostnames.contains(hostname) ||
hostnameVerifier.verify(hostname, session);
}
}
NaiveSSLSocketFactory
public class NaiveSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory sslSocketFactory =
(SSLSocketFactory) SSLSocketFactory.getDefault();
private final SSLContext alwaysAllowSslContext;
private final Set<String> naivelyTrustedHostnames;
public NaiveSSLSocketFactory(String ... naivelyTrustedHostnames)
throws NoSuchAlgorithmException, KeyManagementException {
this.naivelyTrustedHostnames =
Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(naivelyTrustedHostnames)));
alwaysAllowSslContext = SSLContext.getInstance("TLS");
TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
alwaysAllowSslContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public String[] getDefaultCipherSuites() {
return sslSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return (naivelyTrustedHostnames.contains(host))
? alwaysAllowSslContext.getSocketFactory().createSocket(socket, host, port, autoClose)
: sslSocketFactory.createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return (naivelyTrustedHostnames.contains(host))
? alwaysAllowSslContext.getSocketFactory().createSocket(host, port)
: sslSocketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException, UnknownHostException {
return (naivelyTrustedHostnames.contains(host))
? alwaysAllowSslContext.getSocketFactory().createSocket(host, port, localAddress, localPort)
: sslSocketFactory.createSocket(host, port, localAddress, localPort);
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return (naivelyTrustedHostnames.contains(host.getHostName()))
? alwaysAllowSslContext.getSocketFactory().createSocket(host, port)
: sslSocketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
return (naivelyTrustedHostnames.contains(host.getHostName()))
? alwaysAllowSslContext.getSocketFactory().createSocket(host, port, localHost, localPort)
: sslSocketFactory.createSocket(host, port, localHost, localPort);
}
}
参考资料
我大量借鉴了这个答案:
使用 Spring Cloud Netflix >= 1.4 时。4.RELEASE 您还可以执行以下操作:
添加okhttp客户端maven依赖:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
并添加以下属性:
feign.httpclient.disableSslValidation=true
feign.httpclient.enabled=false
feign.okhttp.enabled=true
参考: https://github.com/spring-cloud/spring-cloud-netflix/issues/2729
通过在 application.yaml 中添加以下 属性 来禁用 ssl 验证。
feign.httpclient.disableSslValidation=真
或作为 VM 参数
-Dfeign.httpclient.disableSslValidation=true
feign.httpclient.disableSslValidation = true 对我不起作用。
通过以下代码在配置中创建客户端 bean 有效:
import feign.Client;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.ssl.SSLContexts;
import org.springframework.context.annotation.Bean;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
public class ClientConfiguration {
@Bean
public Client feignClient() {
return new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
}
private SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
return sslContext.getSocketFactory();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
pom.xml 可能需要添加依赖项:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
通过假配置覆盖
@Bean
public Client feignClient()
{
Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
return trustSSLSockets;
}
private SSLSocketFactory getSSLSocketFactory() {
try {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
return sslContext.getSocketFactory();
} catch (Exception exception) {
}
return null;
}
将以下 class 添加到您的存储库并将此 class 用作配置
这段代码对我有用:
@Configuration
public class SSLSocketClient {
@Bean
public Client feignClient() {
return new Client.Default(getSSLSocketFactory(),getHostnameVerifier());
}
//SSLSocketFactory
// Install the all-trusting trust manager
public static SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, getTrustManager(), new SecureRandom());
return sslContext.getSocketFactory();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
//TrustManager
// trust manager that does not validate certificate chains
private static TrustManager[] getTrustManager() {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[]{};
}
}};
return trustAllCerts;
}
//HostnameVerifier
public static HostnameVerifier getHostnameVerifier() {
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession)
{
return true;
}
};
return hostnameVerifier;
}}
当前版本的 spring-cloud-starter-openfeign 抑制主机名验证的工作方式如下。
使用 apache httpclient 时:
在 application.yml 中设置禁用 ssl 验证 属性
feign.httpclient.disable-ssl-validation: true
在pom.xml中添加feign-httpclient依赖。
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
如果您更喜欢 okhttp,则必须使用另一个应用程序启用 okhttp 属性 并添加 feign-okhttp 依赖项:
feign.httpclient.disableSslValidation=true
feign.httpclient.enabled=false
feign.okhttp.enabled=true
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
对于 httpclient5 (hc5),属性 disable-ssl-validation 遗憾的是没有关闭主机名验证(还没有?),这是票证:https://github.com/spring-cloud/spring-cloud-openfeign/issues/625
启用 hc5 的应用程序属性。
feign.httpclient.disableSslValidation=true
feign.httpclient.hc5.enabled=true
要添加的 Maven 依赖项
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hc5</artifactId>
</dependency>
注意: 对我来说棘手的部分是我错过了添加 feign-httpclient 作为依赖项。在这种情况下,使用启用了主机名验证的默认假客户端。