HTTPURLConnection nie podąża za przekierowaniem z HTTP na HTTPS

Nie mogę zrozumieć, dlaczego Java HttpURLConnection nie podąża za przekierowaniem HTTP z HTTP na adres URL HTTPS. Używam poniższego kodu, aby uzyskać stronę w 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();
        }
    }
}

Wyjście tego programu to:

Original URL: http://httpstat.us/301
Connected to: http://httpstat.us/301
HTTP response code received: 301
HTTP response message received: Moved Permanently

Prośba do http://httpstat.us/301 zwraca następującą (skróconą) odpowiedź (która wydaje się absolutnie słuszna!):

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

Niestety, Java HttpURLConnection nie podąża za przekierowaniem!

Zauważ, że jeśli Zmień oryginalny adres URL na HTTPS (https://httpstat.us/301 ), Java będzie podążać za przekierowaniem zgodnie z oczekiwaniami!?

Author: sleske, 2009-12-10

6 answers

Przekierowania są wykonywane tylko wtedy, gdy używają tego samego protokołu. (Patrz metoda followRedirect() w źródle.) Nie ma możliwości wyłączenia tego sprawdzenia.

Mimo, że wiemy, że odzwierciedla HTTP, z punktu widzenia protokołu HTTP, HTTPS jest tylko innym, zupełnie innym, nieznanym protokołem. Byłoby niebezpieczne podążanie za przekierowaniem bez zgody użytkownika.

Załóżmy na przykład, że aplikacja jest skonfigurowana do automatycznego uwierzytelniania klienta. Użytkownik oczekuje, że surfuje anonimowo, bo używa HTTP. Ale jeśli jego klient postępuje zgodnie z HTTPS bez pytania, jego tożsamość zostanie ujawniona serwerowi.

 120
Author: erickson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-26 15:24:28

HttpURLConnection by design nie przekieruje automatycznie z HTTP do HTTPS (lub odwrotnie). Po przekierowaniu może mieć poważne konsekwencje dla bezpieczeństwa. SSL (stąd HTTPS) tworzy sesję, która jest unikalna dla użytkownika. Ta sesja może być ponownie wykorzystana do wielu żądań. W ten sposób serwer może śledzić wszystkie żądania złożone od jednej osoby. Jest to słaba forma tożsamości i nadaje się do wykorzystania. Ponadto, SSL handshake może poprosić o certyfikat klienta. W przypadku wysłania do serwera, następnie tożsamość klienta jest przekazywana serwerowi.

Jak wskazuje erickson , Załóżmy, że aplikacja jest skonfigurowana do automatycznego uwierzytelniania klienta. Użytkownik oczekuje, że surfuje anonimowo, ponieważ używa HTTP. Ale jeśli jego klient postępuje zgodnie z HTTPS bez pytania, jego tożsamość zostanie ujawniona serwerowi.

Programista musi podjąć dodatkowe kroki, aby upewnić się, że poświadczenia, certyfikaty klienta lub identyfikator sesji SSL nie zostaną wysłane przed przekierowaniem z HTTP do HTTPS. Domyślnie jest to wysyłanie tych. Jeśli przekierowanie boli użytkownika, nie podążaj za przekierowaniem. Dlatego automatyczne przekierowanie nie jest obsługiwane.

Z tym zrozumiałym, oto kod, który będzie podążał za przekierowaniami.

  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();
  ...
 55
Author: Nathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-27 15:59:29

Czy może coś się nazywa HttpURLConnection.setFollowRedirects(false)?

Zawsze możesz zadzwonić]}
conn.setInstanceFollowRedirects(true);

Jeśli chcesz się upewnić, że nie wpływasz na resztę zachowania aplikacji.

 27
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-13 10:41:31

Jak wspomnieli niektórzy z was powyżej, setFollowRedirect i setInstanceFollowRedirects działają automatycznie tylko wtedy, gdy przekierowany protokół jest taki sam . ie od http do http i https do https.

SetFolloRedirect jest na poziomie klasy i ustawia to dla wszystkich instancji połączenia url, podczas gdy setInstanceFollowRedirects jest tylko dla danej instancji. W ten sposób możemy mieć różne zachowania dla różnych instancji.

Znalazłem tu bardzo dobry przykład http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/

 7
Author: Shalvika,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-01 06:12:11

Inną opcją może być użycie Apache HttpComponents Client :

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

Przykładowy kod:

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();
 2
Author: Koray Tugay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-09 13:18:47

HTTPUrlConnection nie odpowiada za obsługę odpowiedzi obiektu. Jest to wydajność zgodnie z oczekiwaniami, chwyta zawartość żądanego adresu URL. Do użytkownika funkcjonalności należy interpretacja odpowiedzi. Nie jest w stanie odczytać intencji dewelopera bez specyfikacji.

 -4
Author: monksy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-10 21:41:08