HTTPURLConnection HTTP'den HTTPS'ye Yönlendirmeyi Takip Etmiyor


99

Java'nın neden HttpURLConnectionbir HTTP'den HTTPS URL'ye bir HTTP yeniden yönlendirmesini takip etmediğini anlayamıyorum . Sayfayı https://httpstat.us/ adresinden almak için aşağıdaki kodu kullanıyorum :

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();
        }
    }
}

Bu programın çıktısı:

Orijinal URL: http://httpstat.us/301
Bağlandı: http://httpstat.us/301
Alınan HTTP yanıt kodu: 301
HTTP yanıt mesajı alındı: Kalıcı Olarak Taşındı

Http://httpstat.us/301 adresine yapılan bir istek , aşağıdaki (kısaltılmış) yanıtı döndürür (kesinlikle doğru görünüyor!):

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

Maalesef Java HttpURLConnectionyeniden yönlendirmeyi takip etmiyor!

HTTPS orijinal URL'yi (değiştirirseniz o Not https://httpstat.us/301 ), Java olacak beklendiği gibi yönlendirmeyi izlemek !?


1
Merhaba, Sorunuzu netlik açısından düzenledim ve özellikle HTTPS'ye yönlendirmenin sorun olduğunu belirtmek için. Ayrıca, sorularda bit.ly kullanımı kara listeye alındığından, bit.ly alanını farklı bir alanla değiştirdim. Umarım sorun olmaz, yeniden düzenlemekten çekinmeyin.
sleske

Yanıtlar:


120

Yönlendirmeler, yalnızca aynı protokolü kullanıyorlarsa izlenir. (Bkz yöntemini kaynağındaki.) Bu kontrolü devre dışı bırakmak için bir yolu yoktur.followRedirect()

HTTP protokolü açısından HTTP'yi yansıttığını bilmemize rağmen, HTTPS sadece başka, tamamen farklı, bilinmeyen bir protokoldür. Yönlendirmeyi kullanıcı onayı olmadan takip etmek güvenli olmayacaktır.

Örneğin, uygulamanın istemci kimlik doğrulamasını otomatik olarak gerçekleştirecek şekilde ayarlandığını varsayın. Kullanıcı, HTTP kullandığı için anonim olarak gezinmeyi bekler. Ancak müşterisi HTTPS'yi sormadan takip ederse, kimliği sunucuya açıklanır.


60
Teşekkürler. Doğrulama buldum: bugs.sun.com/bugdatabase/view_bug.do?bug_id=4620571 . Şöyle ki: "Java Networking mühendisleri arasındaki tartışmadan sonra, bir protokolden diğerine, örneğin http'den https'ye ve tam tersi otomatik olarak yönlendirmeyi takip etmememiz gerektiği hissediliyor, bunu yapmanın ciddi güvenlik sonuçları olabilir. Yönlendirme için sunucu yanıtlarını döndürmek için. Yönlendirme bilgileri için yanıt kodunu ve Konum başlığı alanı değerini kontrol edin. Yönlendirmeyi takip etmek uygulamanın sorumluluğundadır. "
Shcheklein

2
Ancak http'den http'ye veya https'den https'ye yönlendirmeyi mi takip ediyor? Bu bile yanlış olur. Değil mi?
Sudarshan Bhat

7
@JoshuaDavis Evet, yalnızca aynı protokole yönlendirmeler için geçerlidir. Bir HttpURLConnectionotomatik yönlendirme bayrağı ayarlanmış olsa bile, farklı bir protokole yönlendirmeleri takip etmeyecek.
erickson

8
Java Ağ mühendisleri bir setFollowTransProtocol (true) seçeneği sunabilir, çünkü ihtiyacımız olursa onu yine de programlayacağız. Bilginize web tarayıcıları, curl ve wget ve daha fazlası HTTP'den HTTPS'ye ve tersi yönde yönlendirmeleri takip edebilir.
supercobra

18
Hiç kimse HTTPS'de otomatik oturum açmayı ayarlamaz ve sonra HTTP'nin "anonim" olmasını beklemez. Bu çok saçma. HTTP'den HTTPS'ye yönlendirmeleri takip etmek tamamen güvenli ve normaldir (tam tersi değil). Bu sadece tipik olarak kötü bir Java API'sidir.
Glenn Maynard

55

Tasarım gereği HttpURLConnection , HTTP'den HTTPS'ye (veya tersi) otomatik olarak yeniden yönlendirme yapmaz. Yönlendirmenin ardından, ciddi güvenlik sonuçları olabilir. SSL (dolayısıyla HTTPS) kullanıcıya özgü bir oturum oluşturur. Bu oturum, birden çok istek için yeniden kullanılabilir. Böylelikle sunucu, tek bir kişiden gelen tüm talepleri takip edebilmektedir. Bu zayıf bir kimlik biçimidir ve istismar edilebilir. Ayrıca, SSL anlaşması istemcinin sertifikasını isteyebilir. Sunucuya gönderilirse, istemcinin kimliği sunucuya verilir.

Ericson'un işaret ettiği gibi , uygulamanın istemci kimlik doğrulamasını otomatik olarak gerçekleştirecek şekilde ayarlandığını varsayalım. Kullanıcı, HTTP kullandığı için anonim olarak gezinmeyi bekler. Ancak müşterisi HTTPS'yi sormadan takip ederse, kimliği sunucuya açıklanır.

Programcı, HTTP'den HTTPS'ye yönlendirmeden önce kimlik bilgilerinin, istemci sertifikalarının veya SSL oturum kimliğinin gönderilmemesini sağlamak için fazladan adımlar atmalıdır. Varsayılan, bunları göndermektir. Yönlendirme kullanıcıya zarar veriyorsa, yönlendirmeyi takip etmeyin. Bu nedenle otomatik yeniden yönlendirme desteklenmez.

Bu anlaşıldığında, yönlendirmeleri takip edecek kod burada.

  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();
  ...

Bu, 1'den fazla yeniden yönlendirme için çalışan tek çözümdür. Teşekkür ederim!
Roger Alien

Bu, çoklu yeniden yönlendirme için güzel çalışır (HTTPS API -> HTTP -> HTTP görüntüsü)! Mükemmel basit çözüm.
EricH206

1
@Nathan - detaylar için teşekkürler, ama hala satın almıyorum. Örneğin, herhangi bir kimlik bilgisinin veya müşteri sertifikasının gönderilip gönderilmediği müşterinin kontrolü altındaysa. Canınız yanarsa, yapmayın (bu durumda yönlendirmeyi takip etmeyin).
Julian Reschke

1
Ben sadece location = URLDecoder.decode(location...kısmını anlamıyorum . Bu, çalışan kodlanmış göreceli bir parçayı (benim durumumda boşluk = +) çalışmayan bir parçaya çözer. Onu çıkardıktan sonra benim için iyiydi.
Niek

@Niek Neden ihtiyacın olmadığından emin değilim ama ihtiyacım var.
Nathan

27

HttpURLConnection.setFollowRedirects(false)Şans eseri bir şey çağrıldı mı?

Her zaman arayabilirsin

conn.setInstanceFollowRedirects(true);

Uygulamanın geri kalan davranışını etkilemediğinizden emin olmak istiyorsanız.


Ooo ... bunu bilmiyordum ... Güzel bul ... Böyle bir mantık varken sınıfı araştırmak üzereydim .... Tek sorumluluğu veren o başlığı döndürmek mantıklı geliyor müdür .... şimdi C # sorularını yanıtlamaya geri dön: P [Şaka yapıyorum]
keşiş

2
SetFollowRedirects () 'in bir örnekte değil, sınıfta çağrılması gerektiğini unutmayın.
karlbecker_com

3
@dldnh: karlbecker_com, setFollowRedirectstürü arama konusunda kesinlikle haklıyken setInstanceFollowRedirects, bir örnek yöntemdir ve türe göre çağrılamaz .
Jon Skeet

1
uggh, bunu nasıl yanlış anladım. yanlış düzenleme için üzgünüm. Ayrıca geri almayı denedim ve bunu nasıl çözdüğümden emin değilim.
dldnh

7

Yukarıda bazılarınız tarafından belirtildiği gibi, setFollowRedirect ve setInstanceFollowRedirects yalnızca yeniden yönlendirilen protokol aynı olduğunda otomatik olarak çalışır. yani http'den http'ye ve https'den https'ye.

setFolloRedirect sınıf düzeyindedir ve bunu url bağlantısının tüm örnekleri için ayarlar, setInstanceFollowRedirects ise yalnızca belirli bir örnek içindir. Bu şekilde, farklı durumlar için farklı davranışlara sahip olabiliriz.

Burada çok iyi bir örnek buldum http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/


2

Diğer bir seçenek de Apache HttpComponents İstemcisini kullanmak olabilir :

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

Basit 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();

-4

HTTPUrlConnection, nesnenin yanıtının işlenmesinden sorumlu değildir. Beklendiği gibi performanstır, istenen URL'nin içeriğini alır. Yanıtı yorumlama işlevinin kullanıcısı size bağlıdır. Spesifikasyon olmadan geliştiricinin niyetlerini okuyamaz.


7
Neden bu durumdaInstanceFollowRedirects'i ayarladı? ))
Shcheklein

Tahminim, daha sonra eklenmesi önerilen bir özellikti, mantıklı geliyor .. yorumum daha çok yansıtıldı ... sınıf gidip web içeriğini alıp geri getirmek için tasarlandı ... insanlar bunu yapmak isteyebilir HTTP 200 olmayan mesajlar alın.
Monksy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.