2013-07-12 3 views
23

Может ли кто-нибудь предоставить мне образец кода для доступа к URL-адресу сервиса обслуживания, защищенному https, с использованием шаблона весеннего отдыха.Доступ к службе поддержки Https с использованием Spring RestTemplate

У меня есть сертификат, имя пользователя и пароль. Базовая проверка подлинности используется на стороне сервера, и я хочу создать клиента, который может подключиться к этому серверу, используя предоставленный сертификат, имя пользователя и пароль (при необходимости).

ответ

14

Вот некоторый код, который даст вам общую идею.

Чтобы доверять сертификату, вам необходимо создать пользовательскую форму ClientHttpRequestFactory. Это выглядит следующим образом:

final ClientHttpRequestFactory clientHttpRequestFactory = 
     new MyCustomClientHttpRequestFactory(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER, serverInfo); 
    restTemplate.setRequestFactory(clientHttpRequestFactory); 

Это реализация для MyCustomClientHttpRequestFactory:

public class MyCustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory { 

private final HostnameVerifier hostNameVerifier; 
private final ServerInfo serverInfo; 

public MyCustomClientHttpRequestFactory (final HostnameVerifier hostNameVerifier, 
    final ServerInfo serverInfo) { 
    this.hostNameVerifier = hostNameVerifier; 
    this.serverInfo = serverInfo; 
} 

@Override 
protected void prepareConnection(final HttpURLConnection connection, final String httpMethod) 
    throws IOException { 
    if (connection instanceof HttpsURLConnection) { 
     ((HttpsURLConnection) connection).setHostnameVerifier(hostNameVerifier); 
     ((HttpsURLConnection) connection).setSSLSocketFactory(initSSLContext() 
      .getSocketFactory()); 
    } 
    super.prepareConnection(connection, httpMethod); 
} 

private SSLContext initSSLContext() { 
    try { 
     System.setProperty("https.protocols", "TLSv1"); 

     // Set ssl trust manager. Verify against our server thumbprint 
     final SSLContext ctx = SSLContext.getInstance("TLSv1"); 
     final SslThumbprintVerifier verifier = new SslThumbprintVerifier(serverInfo); 
     final ThumbprintTrustManager thumbPrintTrustManager = 
      new ThumbprintTrustManager(null, verifier); 
     ctx.init(null, new TrustManager[] { thumbPrintTrustManager }, null); 
     return ctx; 
    } catch (final Exception ex) { 
     LOGGER.error(
      "An exception was thrown while trying to initialize HTTP security manager.", ex); 
     return null; 
    } 
} 

В этом случае мой serverInfo объект содержит отпечаток сервера. Вам необходимо реализовать интерфейс TrustManager, чтобы получить SslThumbprintVerifier или любым другим способом, который вы хотите проверить на свой сертификат (вы также можете также вернуть true).

Значение org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER позволяет использовать все имена хостов. Если вам нужно подтвердить имя хоста, , вам нужно будет реализовать его по-разному.

Я не уверен насчет пользователя и пароля и как вы его реализовали. Часто, необходимо добавить заголовок к restTemplate с именем Authorization со значением, которое выглядит так: Base: <encoded user+password>. user+password должен быть закодирован Base64.

+0

Много моего кода было взято отсюда: http://stackoverflow.com/questions/15544116/sslhandshakeexception -received-fatal-alert-handshake-failure-when-setting-ciph – Avi

+0

Выглядит довольно чисто, за исключением «System.setProperty» («https.protocols», «TLSv1»); линия. Есть ли способ, не устанавливающий некоторые системные вещи? У меня такая же проблема, и я хочу изолировать проблему, связанную с сертификатом, с одним компонентом. – Ruslan

+1

@ Руслан - Вау, ты отвел меня с этим ответом.К сожалению, это было слишком давно, и с тех пор я уже сменил два рабочих места, поэтому у меня нет исходного кода, и я не мог вспомнить, почему я делал так, как сделал. Я почти уверен, что есть способ обойти это, я постараюсь посмотреть, смогу ли я найти другой способ, и если так, я отправлю его здесь. – Avi

4

Вот что у меня получилось для аналогичной проблемы. Идея такая же, как в ответе @ Avi, но я также хотел избежать статического «System.setProperty» («https.protocols», «TLSv1») », так что любые настройки не повлияют на систему. Вдохновленный ответом отсюда http://www.coderanch.com/t/637177/Security/Disabling-handshake-message-Java

public class MyCustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory { 

@Override 
protected void prepareConnection(HttpURLConnection connection, String httpMethod) { 
    try { 
     if (!(connection instanceof HttpsURLConnection)) { 
      throw new RuntimeException("An instance of HttpsURLConnection is expected"); 
     } 

     HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; 

     TrustManager[] trustAllCerts = new TrustManager[]{ 
       new X509TrustManager() { 
        public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
         return null; 
        } 

        public void checkClientTrusted(X509Certificate[] certs, String authType) { 
        } 

        public void checkServerTrusted(X509Certificate[] certs, String authType) { 
        } 

       } 
     }; 
     SSLContext sslContext = SSLContext.getInstance("SSL"); 
     sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); 
     httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); 

     httpsConnection.setHostnameVerifier((hostname, session) -> true); 

     super.prepareConnection(httpsConnection, httpMethod); 
    } catch (Exception e) { 
     throw Throwables.propagate(e); 
    } 
} 

/** 
* We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); 
* see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section) 
*/ 
private static class MyCustomSSLSocketFactory extends SSLSocketFactory { 

    private final SSLSocketFactory delegate; 

    public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { 
     this.delegate = delegate; 
    } 

    @Override 
    public String[] getDefaultCipherSuites() { 
     return delegate.getDefaultCipherSuites(); 
    } 

    @Override 
    public String[] getSupportedCipherSuites() { 
     return delegate.getSupportedCipherSuites(); 
    } 

    @Override 
    public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException { 
     final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose); 
     return overrideProtocol(underlyingSocket); 
    } 

    @Override 
    public Socket createSocket(final String host, final int port) throws IOException { 
     final Socket underlyingSocket = delegate.createSocket(host, port); 
     return overrideProtocol(underlyingSocket); 
    } 

    @Override 
    public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException { 
     final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); 
     return overrideProtocol(underlyingSocket); 
    } 

    @Override 
    public Socket createSocket(final InetAddress host, final int port) throws IOException { 
     final Socket underlyingSocket = delegate.createSocket(host, port); 
     return overrideProtocol(underlyingSocket); 
    } 

    @Override 
    public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException { 
     final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); 
     return overrideProtocol(underlyingSocket); 
    } 

    private Socket overrideProtocol(final Socket socket) { 
     if (!(socket instanceof SSLSocket)) { 
      throw new RuntimeException("An instance of SSLSocket is expected"); 
     } 
     ((SSLSocket) socket).setEnabledProtocols(new String[] {"SSLv3"}); 
     return socket; 
    } 
} 
} 
2

Один пункт от меня. Я использовал взаимную аутентификацию сертификата с помощью микросервисов с пружинной загрузкой. Для меня работают следующие ключи: keyManagerFactory.init (...) и sslcontext.init (keyManagerFactory.getKeyManagers(), null, новые SecureRandom()) строки кода без них, по крайней мере, для меня все не работает , Сертификаты упакованы PKCS12.

@Value("${server.ssl.key-store-password}") 
private String keyStorePassword; 
@Value("${server.ssl.key-store-type}") 
private String keyStoreType; 
@Value("${server.ssl.key-store}") 
private Resource resource; 

private RestTemplate getRestTemplate() throws Exception { 
    return new RestTemplate(clientHttpRequestFactory()); 
} 

private ClientHttpRequestFactory clientHttpRequestFactory() throws Exception { 
    return new HttpComponentsClientHttpRequestFactory(httpClient()); 
} 

private HttpClient httpClient() throws Exception { 

    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); 
    KeyStore trustStore = KeyStore.getInstance(keyStoreType); 

    if (resource.exists()) { 
     InputStream inputStream = resource.getInputStream(); 

     try { 
      if (inputStream != null) { 
       trustStore.load(inputStream, keyStorePassword.toCharArray()); 
       keyManagerFactory.init(trustStore, keyStorePassword.toCharArray()); 
      } 
     } finally { 
      if (inputStream != null) { 
       inputStream.close(); 
      } 
     } 
    } else { 
     throw new RuntimeException("Cannot find resource: " + resource.getFilename()); 
    } 

    SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build(); 
    sslcontext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom()); 
    SSLConnectionSocketFactory sslConnectionSocketFactory = 
      new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1.2"}, null, getDefaultHostnameVerifier()); 

    return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build(); 
} 
16
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
    keyStore.load(new FileInputStream(new File(keyStoreFile)), keyStorePassword.toCharArray()); 

    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
      new SSLContextBuilder() 
        .loadTrustMaterial(null, new TrustSelfSignedStrategy()) 
        .loadKeyMaterial(keyStore, keyStorePassword.toCharArray()) 
        .build(), 
      NoopHostnameVerifier.INSTANCE); 

    HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build(); 

    ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); 
    RestTemplate restTemplate = new RestTemplate(requestFactory); 
    MyRecord record = restTemplate.getForObject(uri, MyRecord.class); 
    LOG.debug(record.toString()); 
+0

.loadKeyМатериал может быть ненужным, чтобы указать – nahab

+0

простое и прямое решение .. –

0

Это решение, не устаревшего класса или метода:

CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); 

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); 
requestFactory.setHttpClient(httpClient); 

RestTemplate restTemplate = new RestTemplate(requestFactory); 
Смежные вопросы