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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1884230/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 23:32:52  来源:igfitidea点击:

HTTPURLConnection Doesn't Follow Redirect from HTTP to HTTPS

javaredirecthttpshttpurlconnectionhttp-redirect

提问by Shcheklein

I can't understand why Java's HttpURLConnectiondoes 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/:

我不明白为什么 JavaHttpURLConnection不遵循从 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();
        }
    }
}

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/301returns the following (shortened) response (which seems absolutely right!):

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

Unfortunately, Java's HttpURLConnectiondoes not follow the redirect!

不幸的是,JavaHttpURLConnection不遵循重定向!

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

请注意,如果将原始 URL 更改为 HTTPS ( https://httpstat.us/301),Java将按预期进行重定向!?

采纳答案by erickson

Redirects are followed only if they use the same protocol. (See the followRedirect()methodin the source.) There is no way to disable this check.

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

Even though we know it mirrors HTTP, from the HTTP protocol point of view, HTTPS is just some other, completely different, unknown protocol. It would be unsafe to follow the redirect without user approval.

尽管我们知道它反映了 HTTP,但从 HTTP 协议的角度来看,HTTPS 只是其他一些完全不同的未知协议。在没有用户批准的情况下跟随重定向是不安全的。

For example, suppose the application is set up to perform client authentication automatically. The user expects to be surfing anonymously because he's using HTTP. But if his client follows HTTPS without asking, his identity is revealed to the server.

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

回答by monksy

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.

HTTPUrlConnection 不负责处理对象的响应。它是预期的性能,它获取请求的 URL 的内容。您是功能的用户来解释响应。没有规范就无法解读开发者的意图。

回答by Jon Skeet

Has something called HttpURLConnection.setFollowRedirects(false)by any chance?

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

You could always call

你可以随时打电话

conn.setInstanceFollowRedirects(true);

if you want to make sure you don't affect the rest of the behaviour of the app.

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

回答by Shalvika

As mentioned by some of you above, the setFollowRedirect and setInstanceFollowRedirects only work automatically when the redirected protocol is same . ie from http to http and https to https.

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

setFolloRedirect is at class level and sets this for all instances of the url connection, whereas setInstanceFollowRedirects is only for a given instance. This way we can have different behavior for different instances.

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

I found a very good example here http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/

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

回答by Nathan

HttpURLConnection by designwon't automatically redirect from HTTP to HTTPS (or vice versa). Following the redirect may have serious security consequences. SSL (hence HTTPS) creates a session that is unique to the user. This session can be reused for multiple requests. Thus, the server can track all of the requests made from a single person. This is a weak form of identity and is exploitable. Also, the SSL handshake can ask for the client's certificate. If sent to the server, then the client's identity is given to the server.

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

As ericksonpoints out, suppose the application is set up to perform client authentication automatically. The user expects to be surfing anonymously because he's using HTTP. But if his client follows HTTPS without asking, his identity is revealed to the server.

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

The programmer has to take extra steps to ensure that credentials, client certificates or SSL session id will not be sent before redirecting from HTTP to HTTPS. The default is to send these. If the redirection hurts the user, do not follow the redirection. This is why automatic redirect is not supported.

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

With that understood, here's the code which will follow the redirects.

理解了这一点后,这是将遵循重定向的代码。

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

回答by Koray Tugay

Another option can be to use Apache HttpComponents Client:

另一种选择是使用Apache HttpComponents Client

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

Sample code:

示例代码:

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();