프록시를 사용한 Java의 HTTP 요청

HTTP를 통한 데이터 액세스는 매일 더 일반적입니다. API든 웹페이지든 애플리케이션 간의 상호 통신이 증가하고 있습니다. 그리고 웹사이트 스크래핑.

Java에서 HTTP 호출을 수행하기 위한 간편한 기본 제공 솔루션은 없습니다. 많은 패키지가 몇 가지 관련 기능을 제공하지만 하나를 선택하기가 쉽지 않습니다. 특히 인증된 프록시를 통한 연결과 같은 추가 기능이 필요한 경우.

Apache HttpComponents 프로젝트의 일부인 fluent.Request 를 사용하여 기본 요청에서 고급 기능으로 이동합니다.

직접 요청



첫 번째 단계는 원하는 페이지를 요청하는 것입니다. 데모용으로 httpbin을 사용하겠습니다. 헤더와 원본 IP를 표시하여 요청이 성공했는지 확인할 수 있습니다.
Request 를 가져와 대상 페이지를 가져오고 결과를 문자열로 추출해야 합니다. 패키지는 이러한 경우 및 더 많은 경우에 대한 방법을 제공합니다. 마지막으로 응답을 인쇄합니다.

import org.apache.hc.client5.http.fluent.Request;

public class TestRequest {
    public static void main(final String... args) throws Exception {
        String url = "http://httpbin.org/anything";

        String response = Request
                .get(url) // use GET HTTP method
                .execute() // perform the call
                .returnContent() // handle and return response
                .asString(); // convert response to string

        System.out.println(response);
    }
}


응답을 처리하거나 오류를 확인하지 않습니다. 실제 사용 사례의 단순화된 버전입니다.

그러나 결과에서 요청이 성공했음을 알 수 있으며 IP가 원본으로 표시됩니다. 잠시 후에 해결해 드리겠습니다.

프록시 요청



보안이나 익명성과 같은 HTTP 요청에 프록시를 추가하는 데는 여러 가지 이유가 있습니다. 어쨌든 Java 라이브러리는 (보통) 프록시 추가를 복잡하게 만듭니다.

우리의 경우 인증이 필요하지 않은 한 프록시 URL과 함께 viaProxy를 사용할 수 있습니다. 나중에 자세히 설명합니다.

지금은 무료 목록의 프록시를 사용합니다. 이들free proxies이 작동하지 않을 수 있습니다. 수명이 짧습니다.

import org.apache.hc.client5.http.fluent.Request;

public class TestRequest {
    public static void main(final String... args) throws Exception {
        String url = "http://httpbin.org/anything";
        String proxy = "http://169.57.1.85:8123"; // Free proxy

        String response = Request.get(url)
                .viaProxy(proxy) // will set the passed proxy
                .execute().returnContent().asString();

        System.out.println(response);
    }
}


인증을 통한 프록시



ZenRows과 같은 유료 또는 개인 프록시 공급자는 각 호출에서 인증을 자주 사용합니다. 때로는 IP 허용 목록을 통해 수행되지만 Proxy-Authorization 헤더와 같은 다른 수단을 사용하는 경우가 많습니다.

적절한 인증 방법 없이 프록시를 호출하면 Exception in thread "main" org.apache.hc.client5.http.HttpResponseException: status code: 407, reason phrase: Proxy Authentication Required 오류가 발생합니다.

예제에 따라 인증과 프록시를 호스트로 전달하는 두 가지가 필요합니다.

Proxy-Authorization 에는 base64로 인코딩된 사용자 및 비밀번호가 포함되어 있습니다.

그런 다음 viaProxy가 사용자 및 암호가 포함된 URL을 허용하지 않으므로 프록시를 가져오는 방법을 변경해야 합니다. 이를 위해 전체 URL을 전달하는 새HttpHost를 생성합니다. 내부적으로 문제를 처리하고 불필요한 부분을 생략합니다.

import java.net.URI;
import java.util.Base64;

import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.http.HttpHost;

public class TestRequest {
    public static void main(final String... args) throws Exception {
        String url = "http://httpbin.org/anything";
        URI proxyURI = new URI("http://YOUR_API_KEY:@proxy.zenrows.com:8001"); // Proxy URL as given by the provider
        String basicAuth = new String(
            Base64.getEncoder() // get the base64 encoder
            .encode(
                proxyURI.getUserInfo().getBytes() // get user and password from the proxy URL
            ));
        String response = Request.get(url)
                .addHeader("Proxy-Authorization", "Basic " + basicAuth) // add auth
                .viaProxy(HttpHost.create(proxyURI)) // will set the passed proxy as a host
                .execute().returnContent().asString();

        System.out.println(response);
    }
}


SSL 인증서 무시



SSL(https) 연결에 프록시를 추가할 때 라이브러리는 인증서에 대한 경고/오류를 발생시키는 경향이 있습니다. 보안 관점에서 보면 정말 대단합니다! 우리는 피하고 싶은 사이트가 표시되거나 리디렉션되는 것을 피합니다.

그러나 우리 자신의 프록시를 통해 연결을 강제하는 것은 어떻습니까? 이러한 경우에는 보안 위험이 없으므로 이러한 경고를 무시하려고 합니다. 즉, 다시 말하지만 Java에서는 쉬운 작업이 아닙니다.

오류는 다음과 같습니다. Exception in thread "main" javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target .

이 경우 대상 URL을 https로 전환하여 수정합니다. 또한 다음에 만들 도우미 메서드를 호출합니다. 기본 기능에서 다른 변경 사항은 없습니다.

public class TestRequest {
    public static void main(final String... args) throws Exception {
        ignoreCertWarning(); // new method that will ignore certificate warnings

        String url = "https://httpbin.org/anything"; // switch to https
        // ...
    }
}


이제 복잡하고 장황한 부분으로 넘어갑니다. SSL 컨텍스트와 가짜 인증서를 만들어야 합니다. 보시다시피 인증서 관리자와 해당 메서드는 아무 작업도 수행하지 않습니다. 내부 작업을 우회하여 문제를 피할 수 있습니다. 마지막으로 생성된 가짜 인증서로 컨텍스트를 초기화하고 기본값으로 설정합니다. 이제 가도 좋습니다!

import java.security.cert.X509Certificate;
import javax.net.ssl.*;

public class TestRequest {
    // ...
    private static void ignoreCertWarning() {
        SSLContext ctx = null;
        TrustManager[] trustAllCerts = new X509TrustManager[] { new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {return null;}
            public void checkClientTrusted(X509Certificate[] certs, String authType) {}
            public void checkServerTrusted(X509Certificate[] certs, String authType) {}
        } };

        try {
            ctx = SSLContext.getInstance("SSL");
            ctx.init(null, trustAllCerts, null);
            SSLContext.setDefault(ctx);
        } catch (Exception e) {}
    }
}


결론



Java에서 데이터 액세스(또는 스크래핑)는 복잡하고 장황해질 수 있습니다. 그러나 올바른 도구와 라이브러리를 사용하여 인증서의 장황함을 길들일 수 있었습니다.

나중에 이 주제로 다시 돌아올 수 있습니다. HttpComponents 라이브러리는 비동기 및 다중 스레드 실행과 같은 매력적인 기능을 제공합니다.

좋은 웹페이지 즐겨찾기