如何使用Jsoup通过HTTPS连接?

时间:2011-10-12 17:29:23

标签: java android https web-scraping jsoup

它在HTTP上运行良好,但是当我尝试使用HTTPS源时,它会抛出以下异常:

10-12 13:22:11.169: WARN/System.err(332): javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
10-12 13:22:11.179: WARN/System.err(332):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:477)
10-12 13:22:11.179: WARN/System.err(332):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:328)
10-12 13:22:11.179: WARN/System.err(332):     at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.setupSecureSocket(HttpConnection.java:185)
10-12 13:22:11.179: WARN/System.err(332):     at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:433)
10-12 13:22:11.189: WARN/System.err(332):     at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeConnection(HttpsURLConnectionImpl.java:378)
10-12 13:22:11.189: WARN/System.err(332):     at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:205)
10-12 13:22:11.189: WARN/System.err(332):     at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:152)
10-12 13:22:11.189: WARN/System.err(332):     at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:377)
10-12 13:22:11.189: WARN/System.err(332):     at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:364)
10-12 13:22:11.189: WARN/System.err(332):     at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:143)

以下是相关代码:

try {
    doc = Jsoup.connect("https url here").get();
} catch (IOException e) {
    Log.e("sys","coudnt get the html");
    e.printStackTrace();
}

10 个答案:

答案 0 :(得分:53)

如果您想以正确的方式进行操作,并且/或者您只需要处理一个站点,那么您基本上需要获取相关网站的SSL证书并将其导入Java密钥库。这将生成一个JKS文件,您在使用Jsoup(或java.net.URLConnection)之前将其设置为SSL信任库。

您可以从webbrowser商店获取证书。我们假设您使用的是Firefox。

  1. 使用Firefox访问相关网站,这是您的https://web2.uconn.edu/driver/old/timepoints.php?stopid=10
  2. 在地址栏中左侧,您会看到蓝色的“uconn.edu”(这表示有效的SSL证书)
  3. 点击它以获取详细信息,然后单击更多信息按钮。
  4. 在出现的安全对话框中,单击查看证书按钮。
  5. 在显示的证书面板中,转到详细信息标签。
  6. 单击证书层次结构中最深的项目,在本例中为“web2.uconn.edu”,最后单击导出按钮。
  7. 现在你有一个web2.uconn.edu.crt文件。

    接下来,打开命令提示符并使用keytool命令(它是JRE的一部分)将其导入Java密钥库:

    keytool -import -v -file /path/to/web2.uconn.edu.crt -keystore /path/to/web2.uconn.edu.jks -storepass drowssap
    

    -file必须指向您刚刚下载的.crt文件的位置。 -keystore必须指向生成的.jks文件的位置(您又要将其设置为SSL信任存储区)。 -storepass是必需的,只要输入至少6个字符,您就可以输入所需的密码。

    现在,您有一个web2.uconn.edu.jks文件。您最终可以在连接之前将其设置为SSL信任存储,如下所示:

    System.setProperty("javax.net.ssl.trustStore", "/path/to/web2.uconn.edu.jks");
    Document document = Jsoup.connect("https://web2.uconn.edu/driver/old/timepoints.php?stopid=10").get();
    // ...
    

    作为一种完全不同的替代方案,特别是当您需要处理多个站点(即您正在创建万维网爬虫)时,您还可以指示Jsoup(基本上,java.net.URLConnection)盲目信任所有SSL证书。另请参阅本答复最底部的“处理不受信任或配置错误的HTTPS站点”部分:Using java.net.URLConnection to fire and handle HTTP requests

答案 1 :(得分:12)

在我的情况下,我需要做的就是在我的连接中添加.validateTLSCertificates(false)

Document doc  = Jsoup.connect(httpsURLAsString)
            .timeout(60000).validateTLSCertificates(false).get();

我还必须增加读取超时,但我认为这是无关紧要的

答案 2 :(得分:8)

我偶然发现了我的搜索中的链接问题和想要添加两条信息的答案,因为接受的答案不适合我非常相似的情况,但还有一个额外的解决方案,即使在那个case(cert和hostname与测试系统不匹配)。

  1. 有一个github请求添加这样的功能。所以也许很快就会解决问题:https://github.com/jhy/jsoup/pull/343 编辑:Github请求已解决,禁用证书验证的方法是:validateTLSCertificates(boolean validate)
  2. 基于http://www.nakov.com/blog/2009/07/16/disable-certificate-validation-in-java-ssl-connections/我发现了一个似乎有效的解决方案(至少在我的场景中,jsoup 1.7.3被称为maven任务的一部分)。我将它包装在我在第一个Jsoup.connect()之前调用的方法disableSSLCertCheck()中。
  3. 在使用此方法之前,您应该确定自己了解自己在那里所做的事情 - 不检查SSL证书是一件非常愚蠢的事情。始终为您的服务器使用正确的SSL证书,这些证书由通常接受的CA签名。如果您负担不起普遍接受的CA,请使用正确的SSL证书,但@BalusC接受上述答案。如果您无法配置正确的SSL证书(在生产环境中绝不应该这样),则可以使用以下方法:

        private void disableSSLCertCheck() throws NoSuchAlgorithmException, KeyManagementException {
        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                }
                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                }
            }
        };
    
        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    
        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
    
        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        }
    

答案 3 :(得分:3)

我遇到了同样的问题,但采取了懒惰的路线 - 告诉你的应用忽略了证书并继续进行。

我从这里得到了代码:How do I use a local HTTPS URL in java?

您必须导入这些类才能正常工作:

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

在尝试建立连接之前,只需在某个地方运行该方法即可,无论如何都只信任证书。当然,如果您确实想要确保证书是真实的,但对监控您自己的内部网站等有帮助,那么这没有任何帮助。

答案 4 :(得分:1)

自该解决方案以来:

Document doc  = Jsoup.connect(httpsURLAsString)
            .timeout(60000).validateTLSCertificates(false).get();

现在显示弃用警告,不久将被删除,这里是替代选项(与answer中提出的想法基本相同),但是对于特定的JSoup连接,禁止显示证书警告:

科特林


val document = Jsoup.connect("url")
        .sslSocketFactory(socketFactory())
        .get()


private fun socketFactory(): SSLSocketFactory {
    val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
        @Throws(CertificateException::class)
        override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
        }

        @Throws(CertificateException::class)
        override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
        }

        override fun getAcceptedIssuers(): Array<X509Certificate> {
            return arrayOf()
        }
    })

    try {
        val sslContext = SSLContext.getInstance("TLS")
        sslContext.init(null, trustAllCerts, java.security.SecureRandom())
        return sslContext.socketFactory
    } catch (e: Exception) {
        when (e) {
            is RuntimeException, is KeyManagementException -> {
                throw RuntimeException("Failed to create a SSL socket factory", e)
            }
            else -> throw e
        }
    }
}

Java



 Document document = Jsoup.connect("url")
        .sslSocketFactory(socketFactory())
        .get();


  private SSLSocketFactory socketFactory() {
    TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
      public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
      }

      public void checkClientTrusted(X509Certificate[] certs, String authType) {
      }

      public void checkServerTrusted(X509Certificate[] certs, String authType) {
      }
    }};

    try {
      SSLContext sslContext = SSLContext.getInstance("TLS");
      sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
      return sslContext.getSocketFactory();
    } catch (NoSuchAlgorithmException | KeyManagementException e) {
      throw new RuntimeException("Failed to create a SSL socket factory", e);
    }
  }

NB。如前所述,忽略证书不是一个好主意。

答案 5 :(得分:0)

我不是这个领域的专家,但是在尝试使用java.net API通过HTTPS连接到网站时遇到了类似的异常。当您使用HTTPS访问站点时,浏览器会为您做很多关于SSL证书的工作。但是,当您手动连接到站点(手动使用HTTP请求)时,仍需要完成所有这些工作。现在我不知道所有这些工作究竟是什么,但它与下载证书并将它们放在Java可以找到它们的地方有关。这是一个有希望指向正确方向的链接。

http://confluence.atlassian.com/display/JIRA/Connecting+to+SSL+services

答案 6 :(得分:0)

我遇到了与Jsoup相同的问题,我无法连接并获取https网址的文档,但当我将JDK版本从1.7更改为1.8时,问题得到了解决。

它可以帮到你:)

答案 7 :(得分:0)

我只在开发环境中遇到过这个问题。解决该问题的解决方案只是添加一些标志以忽略对VM的SSL:

-Ddeployment.security.TLSv1.1=false 
-Ddeployment.security.TLSv1.2=false

答案 8 :(得分:0)

在此处测试解决方案后。奇怪的是,Jsoup 中的 sslSocketFactory 设置完全没有用,而且永远不会起作用。因此无需获取和设置 SSLSocketFactory

实际上 Mori 解决方案的后半部分有效。在使用 Jsoup 之前只需要以下内容:

// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};

// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

这是用 Jsoup 1.13.1 测试的。

答案 9 :(得分:-5)

请尝试以下操作(只需将其放在Jsoup.connect("https://example.com")之前:

    Authenticator.setDefault(new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(username, password.toCharArray());
        }
    });