HTTPURLConnection 不遵循从 HTTP 到 HTTPS 的重定向

新手上路,请多包涵

我不明白为什么 Java 的 HttpURLConnection 不遵循从 HTTP 到 HTTPS URL 的 HTTP 重定向。我使用以下代码获取位于 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();
        }
    }
}

这个程序的输出是:

原文网址: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 按照预期进行重定向!?

原文由 Shcheklein 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 880
2 个回答

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

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

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

原文由 erickson 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

正如 erickson 指出的那样,假设应用程序设置为自动执行客户端身份验证。用户希望匿名上网,因为他使用的是 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();
  ...

原文由 Nathan 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题