109

I can't understand why Java's HttpURLConnection does not follow an HTTP redirect from an HTTP to an HTTPS URL. I use the following code to get the page at 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();
        }
    }
}

The output of this program is:

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

A request to http://httpstat.us/301 returns the following (shortened) response (which seems absolutely right!):

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

Unfortunately, Java's HttpURLConnection does not follow the redirect!

Note that if you change the original URL to HTTPS (https://httpstat.us/301), Java will follow the redirect as expected!?

4

6 回答 6

128

仅当它们使用相同的协议时才会遵循重定向。(请参阅源代码中followRedirect()方法。)无法禁用此检查。

尽管我们知道它反映了 HTTP,但从 HTTP 协议的角度来看,HTTPS 只是另一种完全不同的未知协议。在未经用户批准的情况下遵循重定向是不安全的。

例如,假设应用程序设置为自动执行客户端身份验证。用户希望匿名上网,因为他使用的是 HTTP。但是,如果他的客户端在不询问的情况下使用 HTTPS,他的身份就会暴露给服务器。

于 2009-12-10T22:05:46.090 回答
61

HttpURLConnection 按设计不会自动从 HTTP 重定向到 HTTPS(反之亦然)。遵循重定向可能会产生严重的安全后果。SSL(因此是 HTTPS)创建一个用户独有的会话。此会话可重复用于多个请求。因此,服务器可以跟踪来自单个人的所有请求。这是一种弱形式的身份,是可以利用的。此外,SSL 握手可以请求客户端的证书。如果发送到服务器,则将客户端的身份提供给服务器。

正如埃里克森所指出的,假设应用程序设置为自动执行客户端身份验证。用户希望匿名上网,因为他使用的是 HTTP。但是,如果他的客户端在不询问的情况下使用 HTTPS,他的身份就会暴露给服务器。

程序员必须采取额外的步骤来确保在从 HTTP 重定向到 HTTPS 之前不会发送凭据、客户端证书或 SSL 会话 ID。默认是发送这些。如果重定向伤害了用户,请不要遵循重定向。这就是不支持自动重定向的原因。

了解了这一点,下面是跟随重定向的代码。

  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();
  ...
于 2014-09-25T18:59:05.313 回答
27

有什么东西叫HttpURLConnection.setFollowRedirects(false)什么吗?

你可以随时打电话

conn.setInstanceFollowRedirects(true);

如果您想确保不影响应用程序的其余行为。

于 2009-12-10T21:41:17.480 回答
7

正如上面一些人所提到的, setFollowRedirect 和 setInstanceFollowRedirects 仅在重定向协议相同时自动工作。即从http到http和https到https。

setFolloRedirect 处于类级别,并为 url 连接的所有实例设置它,而 setInstanceFollowRedirects 仅用于给定实例。这样我们就可以对不同的实例有不同的行为。

我在这里找到了一个很好的例子 http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/

于 2013-10-01T06:12:11.113 回答
3

另一种选择是使用Apache HttpComponents 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);
final HttpEntity entity = response.getEntity();
final InputStream is = entity.getContent();
于 2018-07-06T16:16:57.853 回答
-5

HTTPUrlConnection is not responsible for handling the response of the object. It is performance as expected, it grabs the content of the URL requested. It is up to you the user of the functionality to interpret the response. It is not able to read the intentions of the developer without specification.

于 2009-12-10T21:41:08.650 回答