2013-07-12 12 views
23

Qualcuno può fornirmi un esempio di codice per accedere all'url di servizio di riposo protetto con https utilizzando il modello di riposo a molla.Accesso al servizio di ripristino Https utilizzando Spring RestTemplate

Ho il certificato, nome utente e password. L'autenticazione di base viene utilizzata sul lato server e voglio creare un client che possa connettersi a quel server utilizzando certificato, nome utente e password forniti (se necessario).

risposta

14

Ecco un codice che ti darà l'idea generale.

È necessario creare un numero personalizzato ClientHttpRequestFactory per fidarsi del certificato. Ecco come si presenta:

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

Questa è l'implementazione per 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; 
    } 
} 

In questo caso il mio oggetto serverInfo contiene l'identificazione del server. È necessario implementare l'interfaccia TrustManager per ottenere il SslThumbprintVerifier o qualsiasi altro metodo che si desidera verificare il certificato (è anche possibile decidere di restituire sempre anche true).

Il valore org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER consente tutti i nomi host. Se è necessario verificare il nome host, , sarà necessario implementarlo in modo diverso.

Non sono sicuro di utente e password e di come è stato implementato. Spesso, è necessario aggiungere un'intestazione allo restTemplate denominato Authorization con un valore simile al seguente: Base: <encoded user+password>. Il user+password deve essere codificato Base64.

+0

Un sacco del mio codice è stato preso da qui: http://stackoverflow.com/questions/15544116/sslhandshakeexception -received-fatal-alert-handshake-failure-when-setting-ciph – Avi

+0

Sembra abbastanza pulito ad eccezione di "System.setProperty (" https.protocols "," TLSv1 ");" linea. C'è un modo senza impostare alcune cose a livello di sistema? Sto riscontrando un problema simile e desidero isolare il problema relativo al certificato su un solo bean. – Ruslan

+1

@Ruslan - Wow, mi hai riportato indietro con questa risposta.Sfortunatamente, è passato troppo tempo e ho già cambiato due postazioni di lavoro da allora, quindi non ho il codice sorgente e non riesco a ricordare perché ho fatto le cose come ho fatto io. Sono abbastanza sicuro che ci fosse un modo per aggirarlo, proverò a vedere se riesco a trovare un altro modo e in tal caso lo posterò qui. – Avi

4

Ecco cosa ho avuto per il problema simile. L'idea è la stessa della risposta di @ Avi, ma volevo anche evitare la static "System.setProperty (" https.protocols "," TLSv1 ");", in modo che eventuali modifiche non influiscano sul sistema. Ispirato da una risposta da qui 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

Un punto da me. Ho usato un'autenticazione cert reciproca con microservizi a molla. Quanto segue sta funzionando per me, le chiavi qui sono keyManagerFactory.init (...) e sslcontext.init (keyManagerFactory.getKeyManagers(), null, nuove SecureRandom()) linee di codice senza che per me le cose non funzionino . I certificati sono confezionati da 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

.loadKeyMaterial potrebbe non essere necessario per indicare la soluzione – nahab

+0

semplice e diretta. –

0

Si tratta di una soluzione con nessuna classe deprecato o un metodo:

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

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

RestTemplate restTemplate = new RestTemplate(requestFactory);