Nie mogę zrozumieć, dlaczego Java HttpURLConnection
nie śledzi przekierowania HTTP z HTTP do adresu URL HTTPS. Używam następującego kodu, aby uzyskać stronę pod adresem 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();
}
}
}
Wynik tego programu to:
Oryginalny adres URL: http://httpstat.us/301 Połączony z: http://httpstat.us/301 Otrzymany kod odpowiedzi HTTP: 301 Otrzymano wiadomość odpowiedzi HTTP: przeniesiono na stałe
Żądanie 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 śledzi przekierowania!
Zwróć uwagę, że jeśli zmienisz oryginalny adres URL na HTTPS ( https://httpstat.us/301 ), Java będzie postępować zgodnie z oczekiwaniami !?
java
redirect
https
httpurlconnection
http-redirect
Shcheklein
źródło
źródło
Odpowiedzi:
Przekierowania są przestrzegane tylko wtedy, gdy używają tego samego protokołu. (Patrz ten
followRedirect()
sposób w źródle). Nie ma sposobu, aby wyłączyć ten czek.Chociaż wiemy, że odzwierciedla HTTP, z punktu widzenia protokołu HTTP, HTTPS jest po prostu innym, zupełnie innym, nieznanym protokołem. Podążanie za przekierowaniem bez zgody użytkownika byłoby niebezpieczne.
Załóżmy na przykład, że aplikacja jest skonfigurowana do automatycznego uwierzytelniania klienta. Użytkownik oczekuje, że będzie surfował anonimowo, ponieważ używa protokołu HTTP. Ale jeśli jego klient korzysta z protokołu HTTPS bez pytania, jego tożsamość zostaje ujawniona serwerowi.
źródło
HttpURLConnection
będzie automatycznie śledzić przekierowań do innego protokołu, nawet jeśli flaga przekierowania jest ustawiona.HttpURLConnection z założenia nie przekierowuje automatycznie z HTTP do HTTPS (i odwrotnie). Podążanie za przekierowaniem 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 wysyłane od jednej osoby. To słaba forma tożsamości i można ją wykorzystać. Ponadto uzgadnianie SSL może wymagać certyfikatu klienta. W przypadku przesłania na serwer tożsamość klienta jest przekazywana serwerowi.
Jak wskazuje erickson , załóżmy, że aplikacja jest skonfigurowana do automatycznego uwierzytelniania klienta. Użytkownik oczekuje, że będzie surfował anonimowo, ponieważ używa protokołu HTTP. Ale jeśli jego klient korzysta z protokołu HTTPS bez pytania, jego tożsamość zostaje 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 są one wysyłane. Jeśli przekierowanie boli użytkownika, nie postępuj zgodnie z przekierowaniem. Dlatego automatyczne przekierowanie nie jest obsługiwane.
Po zrozumieniu tego, 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(); ...
źródło
location = URLDecoder.decode(location...
części. To dekoduje działającą zakodowaną część względną (ze spacją = + w moim przypadku) na niedziałającą. Po usunięciu było dla mnie OK.Czy przypadkiem coś zostało nazwane
HttpURLConnection.setFollowRedirects(false)
?Zawsze możesz zadzwonić
conn.setInstanceFollowRedirects(true);
jeśli chcesz mieć pewność, że nie wpłyniesz na resztę działania aplikacji.
źródło
setFollowRedirects
typu,setInstanceFollowRedirects
jest metodą instancji i nie można go wywołać na typie.Jak niektórzy z was wspomnieli powyżej, setFollowRedirect i setInstanceFollowRedirects działają automatycznie tylko wtedy, gdy przekierowany protokół jest taki sam. tj. z http na http i https na 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 zachowanie w różnych przypadkach.
Znalazłem tutaj bardzo dobry przykład http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/
źródło
Inną opcją może być użycie klienta Apache HttpComponents :
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();
źródło
HTTPUrlConnection nie odpowiada za obsługę odpowiedzi obiektu. Działa zgodnie z oczekiwaniami, przechwytuje zawartość żądanego adresu URL. Interpretacja odpowiedzi zależy od użytkownika funkcji. Nie jest w stanie odczytać zamiarów dewelopera bez specyfikacji.
źródło