[java] HTTPURLConnection이 HTTP에서 HTTPS 로의 리디렉션을 따르지 않음

Java가 HttpURLConnectionHTTP에서 HTTPS URL 로의 HTTP 리디렉션을 따르지 않는 이유를 이해할 수 없습니다 . 다음 코드를 사용하여 https://httpstat.us/ 페이지를 가져옵니다 .

import java.net.URL;
import java.net.HttpURLConnection;
import java.io.InputStream;

public class Tester {

    public static void main(String argv[]) throws Exception{
        InputStream is = null;

        try {
            String httpUrl = "http://httpstat.us/301";
            URL resourceUrl = new URL(httpUrl);
            HttpURLConnection conn = (HttpURLConnection)resourceUrl.openConnection();
            conn.setConnectTimeout(15000);
            conn.setReadTimeout(15000);
            conn.connect();
            is = conn.getInputStream();
            System.out.println("Original URL: "+httpUrl);
            System.out.println("Connected to: "+conn.getURL());
            System.out.println("HTTP response code received: "+conn.getResponseCode());
            System.out.println("HTTP response message received: "+conn.getResponseMessage());
       } finally {
            if (is != null) is.close();
        }
    }
}

이 프로그램의 출력은 다음과 같습니다.

원래 URL : http://httpstat.us/301
연결 : http://httpstat.us/301
수신 된 HTTP 응답 코드 : 301
수신 된 HTTP 응답 메시지 : 영구적으로 이동 됨

http://httpstat.us/301에 대한 요청 은 다음과 같은 (단축 된) 응답을 반환합니다 (절대적으로 옳은 것 같습니다!).

HTTP/1.1 301 Moved Permanently
Cache-Control: private
Content-Length: 21
Content-Type: text/plain; charset=utf-8
Location: https://httpstat.us

불행히도 Java HttpURLConnection는 리디렉션을 따르지 않습니다!

원래 URL을 HTTPS ( https://httpstat.us/301 ) 로 변경하면 Java 예상대로 리디렉션을 따릅니다!?



답변

리디렉션은 동일한 프로토콜을 사용하는 경우에만 수행됩니다. ( 소스 followRedirect()방법 을 참조하십시오 .)이 검사를 비활성화 할 수있는 방법은 없습니다.

HTTP를 미러링한다는 것을 알고 있지만 HTTP 프로토콜 관점에서 보면 HTTPS는 완전히 다른 알려지지 않은 프로토콜 일뿐입니다. 사용자 승인없이 리디렉션을 따르는 것은 안전하지 않습니다.

예를 들어 응용 프로그램이 클라이언트 인증을 자동으로 수행하도록 설정되어 있다고 가정합니다. 사용자는 HTTP를 사용하기 때문에 익명으로 서핑을 기대합니다. 그러나 그의 클라이언트가 묻지 않고 HTTPS를 따르면 그의 신원이 서버에 공개됩니다.


답변

설계 상 HttpURLConnection 은 HTTP에서 HTTPS로 (또는 그 반대로) 자동으로 리디렉션되지 않습니다. 리디렉션 후에는 심각한 보안 문제가 발생할 수 있습니다. SSL (따라서 HTTPS)은 사용자에게 고유 한 세션을 생성합니다. 이 세션은 여러 요청에 재사용 할 수 있습니다. 따라서 서버는 한 사람이 만든 모든 요청을 추적 할 수 있습니다. 이것은 약한 형태의 신원이며 악용 될 수 있습니다. 또한 SSL 핸드 셰이크는 클라이언트의 인증서를 요청할 수 있습니다. 서버로 전송되면 클라이언트의 ID가 서버에 제공됩니다.

으로 에릭슨은 지적, 응용 프로그램이 자동으로 클라이언트 인증을 수행하도록 설정되어 가정합니다. 사용자는 HTTP를 사용하기 때문에 익명으로 서핑을 기대합니다. 그러나 그의 클라이언트가 묻지 않고 HTTPS를 따르면 그의 신원이 서버에 공개됩니다.

프로그래머는 HTTP에서 HTTPS로 리디렉션하기 전에 자격 증명, 클라이언트 인증서 또는 SSL 세션 ID가 전송되지 않도록 추가 단계를 수행해야합니다. 기본값은이를 보내는 것입니다. 리디렉션이 사용자에게 피해를주는 경우 리디렉션을 따르지 마십시오. 이것이 자동 리디렉션이 지원되지 않는 이유입니다.

이를 이해하면 리디렉션을 따르는 코드가 있습니다.

  URL resourceUrl, base, next;
  Map<String, Integer> visited;
  HttpURLConnection conn;
  String location;
  int times;

  ...
  visited = new HashMap<>();

  while (true)
  {
     times = visited.compute(url, (key, count) -> count == null ? 1 : count + 1);

     if (times > 3)
        throw new IOException("Stuck in redirect loop");

     resourceUrl = new URL(url);
     conn        = (HttpURLConnection) resourceUrl.openConnection();

     conn.setConnectTimeout(15000);
     conn.setReadTimeout(15000);
     conn.setInstanceFollowRedirects(false);   // Make the logic below easier to detect redirections
     conn.setRequestProperty("User-Agent", "Mozilla/5.0...");

     switch (conn.getResponseCode())
     {
        case HttpURLConnection.HTTP_MOVED_PERM:
        case HttpURLConnection.HTTP_MOVED_TEMP:
           location = conn.getHeaderField("Location");
           location = URLDecoder.decode(location, "UTF-8");
           base     = new URL(url);
           next     = new URL(base, location);  // Deal with relative URLs
           url      = next.toExternalForm();
           continue;
     }

     break;
  }

  is = conn.openStream();
  ...


답변

혹시 HttpURLConnection.setFollowRedirects(false)뭐라고 불러 ?

당신은 항상 전화 할 수 있습니다

conn.setInstanceFollowRedirects(true);

앱의 나머지 동작에 영향을 미치지 않는지 확인하려는 경우.


답변

위에서 언급 한 것처럼 setFollowRedirect 및 setInstanceFollowRedirects는 리디렉션 된 프로토콜이 동일한 경우에만 자동으로 작동합니다. 즉, http에서 http로, https에서 https로.

setFolloRedirect는 클래스 수준에 있으며 url 연결의 모든 인스턴스에 대해이를 설정하는 반면 setInstanceFollowRedirects는 지정된 인스턴스에만 적용됩니다. 이렇게하면 인스턴스마다 다른 동작을 할 수 있습니다.

http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/ 아주 좋은 예를 찾았습니다 .


답변

또 다른 옵션은 Apache HttpComponents Client 를 사용하는 것입니다 .

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

샘플 코드 :

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("https://media-hearth.cursecdn.com/avatars/330/498/212.png");
CloseableHttpResponse response = httpclient.execute(httpget);
final HttpEntity entity = response.getEntity();
final InputStream is = entity.getContent();


답변

HTTPUrlConnection은 객체의 응답을 처리하지 않습니다. 예상대로 성능이며 요청 된 URL의 내용을 가져옵니다. 응답을 해석하는 것은 기능의 사용자에게 달려 있습니다. 사양이 없으면 개발자의 의도를 읽을 수 없습니다.


답변