2013-07-12 157 views
23

任何人都可以爲我提供一個代碼示例,以訪問使用spring rest模板使用https保護的rest服務url。使用Spring RestTemplate訪問https Rest服務

我有證書,用戶名和密碼。基本身份驗證用於服務器端,我想創建一個客戶端,可以使用提供的證書,用戶名和密碼(如果需要)連接到該服務器。

回答

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允許所有主機名稱。 如果您需要驗證主機名稱 ,則需要以不同的方式實施。

我不確定用戶和密碼以及如何實現它。 通常, 您需要爲名爲AuthorizationrestTemplate添加標頭,其值如下所示:Base: <encoded user+password>user+password必須是Base64編碼。

+0

我的很多代碼是從這裏取:http://stackoverflow.com/questions/15544116/sslhandshakeexception -setupperty(「https.protocols」,「TLSv1」);「接收到致命警報握手失敗時設置ciph – Avi

+0

看起來很乾淨」線。有沒有設置一些系統級的東西的方法?我遇到了類似的問題,希望將與證書相關的問題隔離爲一個bean。 – Ruslan

+1

@Ruslan - 哇,你帶我回答這個問題。不幸的是,那是很久以前的事了,從那時起我已經換了兩個工作場所,所以我沒有源代碼,我不記得爲什麼我按照自己的方式做了事情。我很確定有一種解決方法,我會試着看看我能否找到另一種方式,如果是的話,我會在這裏發佈。 – 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

從我的一點。我使用了spring-boot微服務的相互認證認證。以下是我的工作,這裏的關鍵點是 keyManagerFactory.init(...)和sslcontext.init(keyManagerFactory.getKeyManagers(),null,new 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

.loadKeyMaterial可能不必要指向 – nahab

+0

簡單而直接的解決方案。 –

0

這是一個沒有不贊成使用的類或方法解決:

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

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

RestTemplate restTemplate = new RestTemplate(requestFactory);