URLConnection不遵循重定向

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

我无法理解为什么Java的HttpURLConnection不遵循重定向。我使用以下代码来获取this page

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 bitlyUrl = "http://bit.ly/4hW294";
            URL resourceUrl = new URL(bitlyUrl);
            HttpURLConnection conn = (HttpURLConnection)resourceUrl.openConnection();
            conn.setConnectTimeout(15000);
            conn.setReadTimeout(15000);
            conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11 (.NET CLR 3.5.30729)");
            conn.connect();
            is = conn.getInputStream();
            String res = conn.getURL().toString();
            if (res.toLowerCase().contains("bit.ly"))
                System.out.println("bit.ly is after resolving: "+res);
       }
       catch (Exception e) {
           System.out.println("error happened: "+e.toString());
       }
       finally {
            if (is != null) is.close(); 
        }
    }
}

而且,我收到以下回复(看起来绝对正确!):

GET /4hW294 HTTP/1.1
Host: bit.ly
Connection: Keep-Alive
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; ru-RU; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)
HTTP/1.1 301 Moved
Server: nginx/0.7.42
Date: Thu, 10 Dec 2009 20:28:44 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Location: https://www.myganocafe.com/CafeMacy
MIME-Version: 1.0
Content-Length: 297

不幸的是,res变量包含相同的URL,并且流包含以下内容(显然,Java的HttpURLConnection不遵循重定向!):

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML>
<HEAD>
<TITLE>Moved</TITLE>
</HEAD>
<BODY>
<H2>Moved</H2>
<A HREF="https://www.myganocafe.com/CafeMacy">The requested URL has moved here.</A>
<P ALIGN=RIGHT><SMALL><I>AOLserver/4.5.1 on http://127.0.0.1:7400</I></SMALL></P>
</BODY>
</HTML>
java httpurlconnection
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.