2013-07-12 22 views
23

Czy ktoś może dostarczyć mi przykład kodu, aby uzyskać dostęp do adresu URL usługi odpoczynku zabezpieczonego przy użyciu https przy użyciu szablonu odpoczynku spoczynkowego.Dostęp do usługi Reszta usługi Https przy użyciu Spring RestTemplate

Mam certyfikat, nazwę użytkownika i hasło. Uwierzytelnianie podstawowe jest używane po stronie serwera i chcę utworzyć klienta, który będzie mógł łączyć się z tym serwerem przy użyciu dostarczonego certyfikatu, nazwy użytkownika i hasła (w razie potrzeby).

Odpowiedz

14

Oto kod, który przedstawi ogólny pomysł.

Musisz utworzyć niestandardowy ClientHttpRequestFactory, aby zaufać certyfikatowi. Wygląda to tak:

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

Jest to realizacja dla 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; 
    } 
} 

W tym przypadku moja serverInfo obiekt zawiera odcisk palca serwera. Musisz zaimplementować interfejs TrustManager, aby uzyskać z SslThumbprintVerifier lub dowolną inną metodę, którą chcesz zweryfikować (możesz także zawsze zwrócić true).

Wartość org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER zezwala na wszystkie nazwy hostów. Jeśli musisz zweryfikować nazwę hosta, , musisz wdrożyć ją inaczej.

Nie mam pewności co do użytkownika i hasła oraz sposobu jego wdrożenia. Często należy dodać nagłówek do restTemplate o nazwie Authorization o wartości podobnej do tej: Base: <encoded user+password>. Kodowana user+password musi być Base64.

+0

Dużo mojego kodu został wzięty stąd: http://stackoverflow.com/questions/15544116/sslhandshakeexception -received-fatal-alert-handshake-failure-when-setting-ciph – Avi

+0

Wygląda całkiem czysto, z wyjątkiem "System.setProperty (" https.protocols "," TLSv1 ");" linia. Czy istnieje sposób bez ustawiania pewnych rzeczy na poziomie systemu? Mam podobny problem i chcę wyizolować problem związany z certyfikatem tylko z jednym komponentem bean. – Ruslan

+1

@Ruslan - Wow, zabrałeś mnie z powrotem z tą odpowiedzią.Niestety, było to zbyt dawno temu i od tego czasu zmieniłem już dwa miejsca pracy, więc nie mam kodu źródłowego i nie mogłem sobie przypomnieć, dlaczego robiłem rzeczy tak, jak ja. Jestem prawie pewny, że było na to sposób, spróbuję sprawdzić, czy mogę znaleźć inny sposób, a jeśli tak, to go tutaj zamieściłem. – Avi

4

Oto, co udało mi się rozwiązać w przypadku podobnego problemu. Pomysł jest taki sam jak w odpowiedzi @ Avi, ale chciałem również uniknąć statycznego "System.setProperty (" https.protocols "," TLSv1 ");", więc wszelkie poprawki nie wpłyną na system. Zainspirowany odpowiedzią stąd: 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

Jeden punkt ode mnie. Użyłem uwierzytelniania za pomocą wspólnego certyfikatu z mikroserwisami wiosennym. Poniższe działa dla mnie, klucze punkt tutaj są keyManagerFactory.init (...) i sslcontext.init (keyManagerFactory.getKeyManagers(), null, nowy SecureRandom()) wiersze kodu bez nich conajmniej dla mnie rzeczy nie działa . Certyfikaty są pakowane przez 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

.loadKeyMateriał może być niepotrzebny, aby wskazać proste i proste rozwiązanie w postaci – nahab

+0

.. –

0

Jest to rozwiązanie bez przestarzałej klasy lub metody:

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

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

RestTemplate restTemplate = new RestTemplate(requestFactory); 
Powiązane problemy