13

我怎样才能curl -kfeign客户端实现?

我知道我可以做到这一点。只是想知道是否有办法忽略或禁用。

new Client.Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier)
4

7 回答 7

24

Disclaimer

You should not actually do this for a number of very good reasons. The easiest way to fix SSL issues is to actually follow SSL best practices and use valid certificates. There are excellent projects online such as https://letsencrypt.org/ that will even allow you to get great security for free if the host is publicly accessible (if it has a real hostname that can be verified against).

USE AT YOUR OWN RISK. MAKE SURE YOU UNDERSTAND YOU ARE VIOLATING A LOT OF BEST PRACTICES AND ONLY USE THIS IF YOU UNDERSTAND THAT.

If you cause some type of major problem using this example code, you are liable.

Real talk

I had the same problem dealing with internal (publicly non-accessible) services that I wanted to call from a spring-boot application and I solved it using the following code.

Brief Overview

A great many people will tell you that you can either accept all certificates, hard-code your particular cert in it, or something else. You can actually allow only certain trusted hosts through the codepath, which is what I am attempting here for an additional layer of security.

In this example code, you can pass multiple hosts to the classes and it should allow requests to only those hosts to be issued with invalid certificates, and everything else will go through the normal chain of command.

This is not really production grade code, but hopefully you will get some use out of it.

Enough lecturing, what follows may interest you the most.

The Code

This is using for Java 8 and spring-boot.

Configuration

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

References

I borrowed heavily from this answer:

Trusting all certificates using HttpClient over HTTPS

于 2017-06-21T01:14:42.120 回答
17

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

于 2018-11-12T08:23:15.667 回答
5

通过 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;
}
于 2019-07-22T05:50:49.323 回答
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>
于 2019-06-19T05:30:21.410 回答
2

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

feign.httpclient.disableSslValidation=true

或作为 VM 参数

-Dfeign.httpclient.disableSslValidation=true

于 2018-11-29T08:27:30.640 回答
1

使用当前版本的 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 客户端。

于 2021-11-11T08:50:12.373 回答
0

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

这段代码对我有用:

@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;
}}
于 2021-03-17T10:46:10.593 回答