URLConnection不遵循从HTTP重定向到HTTPS的过程

问题描述 投票:90回答:6

我不明白为什么Java的HttpURLConnection不遵循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没有遵循重定向!

java redirect https httpurlconnection http-redirect
6个回答
109
投票

我认为它不会自动从HTTP重定向到HTTPS(反之亦然)。

尽管我们知道它是镜像HTTP,但从HTTP协议的角度来看,HTTPS只是其他完全不同的未知协议。未经用户批准而进行重定向将是不安全的。

例如,假设应用程序设置为自动执行客户端身份验证。用户期望使用HTTP进行匿名浏览。但是,如果他的客户不经询问就遵循HTTPS,则他的身份将显示给服务器。


50
投票

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

26
投票

偶然有叫HttpURLConnection.setFollowRedirects(false)的东西吗?

您可以随时致电

conn.setInstanceFollowRedirects(true);

[如果您想确保自己不影响应用程序的其他行为。


6
投票

[正如您中的某些人所提到的,只有在重定向的协议相同时,setFollowRedirect和setInstanceFollowRedirects才自动工作。即从http到http,从https到https。

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

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


2
投票

另一个选择是使用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();

-4
投票

HTTPUrlConnection不负责处理对象的响应。这是预期的性能,它将获取所请求URL的内容。解释响应的功能取决于用户。没有规范,它无法读取开发人员的意图。

© www.soinside.com 2019 - 2024. All rights reserved.