bestsource

Spring Rest Template를 사용하여 Https Rest 서비스 액세스

bestsource 2023. 9. 1. 21:11
반응형

Spring Rest Template를 사용하여 Https Rest 서비스 액세스

스프링레스트 템플릿을 사용하여 HTTPS로 보안된 나머지 서비스 URL에 액세스할 수 있는 코드 샘플을 제공할 수 있는 사람이 있습니까?

저는 인증서, 사용자 이름, 비밀번호를 가지고 있습니다.기본 인증은 서버 측에서 사용되며 제공된 인증서, 사용자 이름 및 암호(필요한 경우)를 사용하여 해당 서버에 연결할 수 있는 클라이언트를 만들고자 합니다.

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());

여기 당신에게 일반적인 아이디어를 제공할 몇 가지 코드가 있습니다.

사용자 정의를 생성해야 합니다.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부호화된

사용되지 않는 클래스 또는 메서드가 없는 솔루션입니다. (Java 8 승인)

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

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

RestTemplate restTemplate = new RestTemplate(requestFactory);

참고 정보: NoopHostnameVerifier를 사용하면 보안 위험이 있습니다.

저랑 1점 차이입니다.스프링부트 마이크로 서비스와 상호 인증을 사용했습니다.다음은 저에게 효과가 있습니다. 여기서 핵심 사항은 다음과 같습니다.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();
}

이것이 제가 이와 비슷한 문제로 결론을 내린 것입니다.아이디어는 @Avi의 대답과 같지만, 저는 또한 정적인 "시스템"을 피하고 싶었습니다.시스템에 영향을 미치지 않도록 속성("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;
    }
}
}

다음과 같은 방법으로 SSL을 지원하는 원시 HttpClient를 구성해야 합니다.

@Test
public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_thenCorrect() 
throws ClientProtocolException, IOException {
    CloseableHttpClient httpClient
      = HttpClients.custom()
        .setSSLHostnameVerifier(new NoopHostnameVerifier())
        .build();
    HttpComponentsClientHttpRequestFactory requestFactory 
      = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

    ResponseEntity<String> response 
      = new RestTemplate(requestFactory).exchange(
      urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

보낸이: 배둥

언급URL : https://stackoverflow.com/questions/17619871/access-https-rest-service-using-spring-resttemplate

반응형