【问题标题】:How to ignore SSL cert trust errors in Feign?如何忽略 Feign 中的 SSL 证书信任错误?
【发布时间】:2023-04-07 16:23:01
【问题描述】:

如何在feign 客户端中实现curl -k

我知道我能做到。只是想知道是否有办法忽略或禁用。

new Client.Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier)

【问题讨论】:

    标签: ssl netflix-feign feign


    【解决方案1】:

    免责声明

    您实际上不应该这样做,原因有很多。解决 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);
        }
    }
    

    参考文献

    我从这个答案中大量借鉴:

    Trusting all certificates using HttpClient over HTTPS

    【讨论】:

    • 感谢您的详细回答。
    【解决方案2】:

    当使用 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

    【讨论】:

    • 此解决方案是否会导致任何安全漏洞?
    • disableSslValidation 也适用于 apache httpclient。请参阅下面的答案。
    • @kashiviswanath 好吧...您可以询问禁用 SSL 证书验证是否会导致任何安全漏洞。这些选项正在做这件事;)在我看来,应该根据用例接受委托证书。
    【解决方案3】:

    通过 feign 配置覆盖

    @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;
    }
    

    【讨论】:

      【解决方案4】:

      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>
      

      【讨论】:

      • 我找不到你们大部分的进口产品,你能详细说明你的样品并添加整个需要的部分吗?
      • 您好,感谢您的更新,我没有 apache commons 依赖项。不过,这对我不起作用,我仍然遇到 ssl 验证错误。我终于将证书添加到 jvm 信任库
      【解决方案5】:

      通过在 application.yaml 中添加以下属性来禁用 ssl 验证。

      feign.httpclient.disableSslValidation=true

      或作为 VM 参数

      -Dfeign.httpclient.disableSslValidation=true

      【讨论】:

        【解决方案6】:

        使用当前版本的 spring-cloud-starter-openfeign 抑制主机名验证的工作方式如下。

        使用 apache httpclient 时:

        在 application.yml 中设置 disable-ssl-validation 属性

        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 客户端。

        【讨论】:

          【解决方案7】:

          将以下类添加到您的存储库并使用该类作为配置

          这段代码对我有用:

          @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;
          }}
          

          【讨论】:

            猜你喜欢
            • 2019-09-05
            • 1970-01-01
            • 2015-04-22
            • 1970-01-01
            • 1970-01-01
            • 2011-02-11
            • 2012-08-17
            • 1970-01-01
            相关资源
            最近更新 更多