URLConnection 不遵循重定向
-
19-09-2019 - |
题
我不明白为什么Java HttpURLConnection
不遵循重定向。我使用下面的代码来获取 这一页:
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>
解决方案
我不认为它会自动从 HTTP 重定向到 HTTPS(反之亦然)。
尽管我们知道它镜像 HTTP,但从 HTTP 协议的角度来看,HTTPS 只是其他一些完全不同的未知协议。在未经用户批准的情况下遵循重定向是不安全的。
例如,假设应用程序设置为自动执行客户端身份验证。用户希望匿名冲浪,因为他使用的是 HTTP。但是,如果他的客户端在没有询问的情况下遵循 HTTPS,他的身份就会暴露给服务器。
其他提示
HttpURL连接通过 设计 不会自动从 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();
...
有一个东西叫做 HttpURLConnection.setFollowRedirects(false)
在任何情况下?
你可以随时打电话
conn.setInstanceFollowRedirects(true);
如果您想确保不会影响应用程序的其余行为。
正如上面提到的, setFollowRedirect 和 setInstanceFollowRedirects 仅当重定向协议相同时才会自动工作。即从http 到http 和https 到https。
setFolloRedirect 位于类级别,并为 url 连接的所有实例设置此项,而 setInstanceFollowRedirects 仅针对给定实例。这样我们就可以针对不同的实例有不同的行为。
我在这里找到了一个很好的例子 http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/
另一种选择是使用 Apache HttpComponents 客户端:
<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();
HTTPUrlConnection 不负责处理对象的响应。它的性能符合预期,它抓取了所请求的 URL 的内容。由该功能的用户来解释响应。如果没有规范,它无法读取开发人员的意图。