Вопрос

Я не могу понять, почему Java HttpURLConnection не следует перенаправлению.Я использую следующий код, чтобы получить эта страница:

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 bitlyUrl = "http://bit.ly/4hW294";
            URL resourceUrl = new URL(bitlyUrl);
            HttpURLConnection conn = (HttpURLConnection)resourceUrl.openConnection();
            conn.setConnectTimeout(15000);
            conn.setReadTimeout(15000);
            conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11 (.NET CLR 3.5.30729)");
            conn.connect();
            is = conn.getInputStream();
            String res = conn.getURL().toString();
            if (res.toLowerCase().contains("bit.ly"))
                System.out.println("bit.ly is after resolving: "+res);
       }
       catch (Exception e) {
           System.out.println("error happened: "+e.toString());
       }
       finally {
            if (is != null) is.close(); 
        }
    }
}

Более того, я получаю следующий ответ (он кажется абсолютно правильным!):

GET /4hW294 HTTP/1.1
Host: bit.ly
Connection: Keep-Alive
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; ru-RU; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)
HTTP/1.1 301 Moved
Server: nginx/0.7.42
Date: Thu, 10 Dec 2009 20:28:44 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Location: https://www.myganocafe.com/CafeMacy
MIME-Version: 1.0
Content-Length: 297

К сожалению, res переменная содержит тот же URL-адрес, а поток содержит следующее (очевидно, Java HttpURLConnection не следует перенаправлению!):

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML>
<HEAD>
<TITLE>Moved</TITLE>
</HEAD>
<BODY>
<H2>Moved</H2>
<A HREF="https://www.myganocafe.com/CafeMacy">The requested URL has moved here.</A>
<P ALIGN=RIGHT><SMALL><I>AOLserver/4.5.1 on http://127.0.0.1:7400</I></SMALL></P>
</BODY>
</HTML>
Это было полезно?

Решение

Я не думаю, что он автоматически перенаправит с HTTP на HTTPS (или наоборот).

Несмотря на то, что мы знаем, что он отражает HTTP, с точки зрения протокола HTTP, HTTPS - это просто какой-то другой, совершенно другой, неизвестный протокол.Было бы небезопасно следовать перенаправлению без разрешения пользователя.

Например, предположим, что приложение настроено на автоматическое выполнение аутентификации клиента.Пользователь ожидает, что будет заниматься серфингом анонимно, потому что он использует HTTP.Но если его клиент переходит по протоколу HTTPS без запроса, его личность раскрывается серверу.

Другие советы

HttpURLConnection с помощью Дизайн не будет автоматически перенаправляться с HTTP на HTTPS (или наоборот).Последующее перенаправление может иметь серьезные последствия для безопасности.Протокол SSL (следовательно, HTTPS) создает сеанс, уникальный для пользователя.Этот сеанс может быть повторно использован для нескольких запросов.Таким образом, сервер может отслеживать все запросы, сделанные от одного человека.Это слабая форма идентичности, которую можно использовать.Кроме того, SSL-квитирование может запросить сертификат клиента.Если отправлено на сервер, то идентификатор клиента передается серверу.

Как эриксон указывает, предположим, что приложение настроено на автоматическое выполнение аутентификации клиента.Пользователь ожидает, что будет заниматься серфингом анонимно, потому что он использует HTTP.Но если его клиент переходит по протоколу HTTPS без запроса, его личность раскрывается серверу.

Программист должен предпринять дополнительные шаги, чтобы гарантировать, что учетные данные, клиентские сертификаты или идентификатор сеанса SSL не будут отправлены перед перенаправлением с HTTP на HTTPS.По умолчанию они должны быть отправлены.Если перенаправление причиняет вред пользователю, не выполняйте перенаправление.Вот почему автоматическое перенаправление не поддерживается.

Поняв это, вот код, который будет следовать за перенаправлениями.

  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:

<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-адреса.Интерпретировать ответ должны вы, пользователь функциональности.Он не способен прочитать намерения разработчика без уточнения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top