使Feign客户端从自定义属性

时间:2018-04-13 06:39:54

标签: tls1.2 self-signed spring-cloud-feign

我们的应用中的Feign客户端正在与自签名服务器进行通信。我们可以使Feign客户端使用属性javax.net.ssl.trustStore系统属性来使用自定义信任库。 但是因为我的应用程序还与标准的CA认证站点进行通信,所以不应该覆盖默认的信任库。

如何在不使用javax.net.ssl.trustStore系统属性的情况下使用自定义信任库?或者我如何让我的Feign客户端使用标准javax.net.ssl.trustStore系统属性以外的属性中的信任库?

3 个答案:

答案 0 :(得分:0)

我最终使用以下代码手工制作了我传递给我的Feign客户端的SSLSocketFactory实例,

/**
 * Gets the {@link SSLSocketFactory} instance for the client communication
 * using the given truststore file and password.
 * 
 * Since the instance is used as client, this is instantiated with empty
 * keystore and the truststore represented by the given truststore file.
 * 
 * 
 * @param theTrustStoreFile
 *            The complete file path of the truststore.
 * @return {@link SSLSocketFactory} instance that internally uses the given
 *         truststore.
 * @throws Exception
 *             When there is an error in the creating the
 *             {@link SSLSocketFactory} instance.
 */
public static SSLSocketFactory getClientSSLSocketFactory(File theTrustStoreFile)
        throws Exception
{
    // This supports TLSv1.2
    SSLContext sslContext = SSLContext.getInstance("TLS");

    KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());

    FileInputStream file = getFileInputStream(theTrustStoreFile);
    kStore.load(file, null);

    TrustManagerFactory tmf = TrustManagerFactory
            .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(kStore);

    sslContext.init(new KeyManager[] {}, tmf.getTrustManagers(), null);

    return sslContext.getSocketFactory();
}

/**
 * Reads the file into {@link FileInputStream} instance.
 * 
 * @param file
 *            The file to be read.
 * @return {@link FileInputStream} that represents the file content/
 * @throws Exception
 *             When there is any error in reading the file.
 */
private static FileInputStream getFileInputStream(final File file) throws Exception
{
    return AccessController.doPrivileged(new PrivilegedExceptionAction<FileInputStream>()
    {
        @Override
        public FileInputStream run() throws Exception
        {
            try
            {
                if (file.exists())
                {
                    return new FileInputStream(file);
                } else
                {
                    return null;
                }
            } catch (FileNotFoundException e)
            {
                // couldn't find it, oh well.
                return null;
            }
        }
    });
}

当我实例化我的客户端时,我会这样做,

Feign.builder().client(getClientSSLSocketFactory(trustFile),null)...

gist contains示例代码及其用法。

答案 1 :(得分:0)

这就是我将FeignClient与keystoretruststore一起使用的方式

FeignClient配置

@Configuration
public class TestClientConfig {

    @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 {
                    //Do your validations
                    return true;
                }
            };
            String allPassword = "123456";
            SSLContext sslContext = SSLContextBuilder
                    .create()
                    // .loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.p12"), allPassword.toCharArray(), allPassword.toCharArray())
                    .loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.jks"), allPassword.toCharArray(), allPassword.toCharArray())
                    .loadTrustMaterial(ResourceUtils.getFile("classpath:truststore.jks"), allPassword.toCharArray())
                    .build();
            return sslContext.getSocketFactory();
        } catch (Exception exception) {
            throw new RuntimeException(exception);
        }
    }
}

界面

@FeignClient(name = "Test", url = "https://localhost:8443",configuration=TestClientConfig.class)
public interface TestClient {

    @RequestMapping(method = RequestMethod.GET,value = "/hello")
    String getHello();
}

答案 2 :(得分:0)

感谢 Kannan 和 Niraj 的回答,我设法将我的假客户端配置为使用 customSSLSocketFactory。

我的用例:我有一个 api,需要将 SSL 证书添加到我在 JAVA cacerts 上的本地机器上才能获取它。但是,在云服务器上部署后,我无法添加此证书。因此,我需要配置我的 Feign 客户端以从我的应用程序本身接收自定义信任存储。

这是我的版本

  1. 使用添加的证书创建您自己的信任库 keytool -import -file <filepath-of-certificate> -alias <certificate-alias-name> -keystore <truststore-alias-name> 系统会提示输入密码,设置自己的密码并记住 Truststore 在这种情况下不需要任何扩展

  2. 将此信任库文件复制到您的 Spring Boot 应用程序的资源文件夹

  3. 在 application.properties 或 application.yml 文件中为您的信任库设置类路径和密码。我使用 application.yml 所以例子如下

例如

example-service:
  server: https://example.api.com
  trustStore: classpath:<Your truststore name>
  trustStorePassword: yourpassword

*您也可以跳过第3步,直接传入下面的信任库和密码*

  1. 在 CustomFeignConfig 类中,您可以直接将信任库作为资源传入,并将信任库输入流直接传入 sslsocketfactory。请注意,Bean 表示法对于覆盖默认的 feign 配置至关重要。 我的案例只需要一个信任库,所以我在 sslContext.init()
  2. 上传递了一个空的 new KeyManager[]{}

` 公共类 CustomFeignConfig {

@Bean
public Client feignClient(@Value("${example-service.trustStore}") Resource trustStoreResource,
                          @Value("${example-service.trustStorePassword}") String trustStorePassword) throws Exception {
    try {
        return new Client.Default(
                sslSocketFactory(trustStoreResource.getInputStream(), trustStorePassword),
                null);
    } catch (Exception e) {
        throw new Exception("Error in initializing feign client", e);
    }
}

private static SSLSocketFactory sslSocketFactory(InputStream trustStoreStream, String trustStorePassword)
        throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, KeyManagementException {
    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    TrustManagerFactory tmf = createTrustManager(trustStoreStream, trustStorePassword);
    sslContext.init(new KeyManager[]{}, tmf.getTrustManagers(), null);
    return sslContext.getSocketFactory();
}

private static TrustManagerFactory createTrustManager(InputStream trustStoreStream, String trustStorePassword)
        throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    trustStore.load(trustStoreStream, trustStorePassword.toCharArray());
    TrustManagerFactory tmf = TrustManagerFactory
            .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(trustStore);
    return tmf;
}

}`

  1. 仅将此自定义 feign 配置应用于需要它的 api

@FeignClient(name = "example-service", url = "${example-service.server}", configuration = CustomFeignConfig.class) public interface MyFeignClient {}

相关问题