Вопрос-ответ

HTTPURLConnection Doesn't Follow Redirect from HTTP to HTTPS

HttpURLConnection Не следует за перенаправлением с HTTP на HTTPS

Я не могу понять, почему Java HttpURLConnection не следует за HTTP-перенаправлением с HTTP на HTTPS URL. Я использую следующий код для получения страницы по адресу 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 будет перенаправляться, как и ожидалось!?

Переведено автоматически
Ответ 1

Перенаправления выполняются, только если они используют один и тот же протокол. (См. followRedirect() Метод в исходном коде.) Отключить эту проверку невозможно.

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

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

Ответ 2

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();
...
Ответ 3

Как упоминалось некоторыми из вас выше, setFollowRedirect и setInstanceFollowRedirects работают автоматически, только если перенаправленный протокол тот же . т. е. с http на http и https на https.

setFolloRedirect находится на уровне класса и устанавливает это для всех экземпляров URL-соединения, тогда как setInstanceFollowRedirects - только для данного экземпляра. Таким образом, мы можем иметь разное поведение для разных экземпляров.

Я нашел здесь очень хороший пример http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example /

Ответ 4

Другим вариантом может быть использование Apache HttpContents 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);
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
2023-09-26 00:31 java