【问题标题】:How to connect via HTTPS using Jsoup?如何使用 Jsoup 通过 HTTPS 连接?
【发布时间】:2011-12-06 08:13:10
【问题描述】:

它在 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();
}

【问题讨论】:

  • 只需添加 ignoreHttpErrors(true) 它适用于 v 1.7.2 Jsoup.connect(url).userAgent(userAgent).ignoreHttpErrors(true).get()

标签: java android https web-scraping jsoup


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    您可以从网络浏览器的商店中获取证书。假设您使用的是 Firefox。

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

    现在你有一个web2.uconn.edu.crt 文件。

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

    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

    【讨论】:

    • 刚刚发现了这个问题......我也有同样的问题,但是如果我使用的是 Eclipse,我该如何处理 crt 文件? eclipse的keytool替代品是什么?
    • 显然,firefox 也允许使用域级证书来访问子域。但是,JSoup 不允许这样做。有什么建议可以解决这个问题?
    • 感谢您的提示!加载 .jdk 文件仍然有问题.. 看起来它不包含或无法从外部 /crt 目录访问。文件 f = new File(Environment.getRootDirectory() + "/crt/www.loterie.lu.jks"); if(f.isFile()) Log.i("JSOUP", "找到证书文件"); else Log.i("JSOUP", "ERROR : Certificate file not found "+f.getAbsolutePath());
    • 最深的项目是Thumbprint,不是网站!
    • 谷歌浏览器也可以吗?
    【解决方案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. 有一个 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 任务的一部分)。我将它包装在一个方法 disableSSLCertCheck() 中,我在第一个 Jsoup.connect() 之前调用了该方法。

      在你使用这个方法之前,你应该确定你明白你在那里做什么 - 不检查 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);
          }
      

      【讨论】:

      • 对于下一位读者...请注意:这会更改应用程序中创建 HttpsURLConnection 实例的任何类的行为,而不仅仅是在您运行它的类中。
      • 如何将此解决方案与 Jsoup.connect(httpsurl).get() 方法集成?
      • @Luke 解析 HttpsURLConnection 的结果:jsoupDoc = Jsoup.parse(urlConnection.getInputStream()
      【解决方案5】:

      尝试关注(只需放在Jsoup.connect("https://example.com")之前:

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

      【讨论】:

        【解决方案6】:

        就我而言,我需要做的就是在我的连接中添加 .validateTLSCertificates(false)

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

        我还不得不增加读取超时,但我认为这无关紧要

        【讨论】:

        【解决方案7】:

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

        它可以帮助你:)

        【讨论】:

          【解决方案8】:

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

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

          【讨论】:

            【解决方案9】:

            要抑制特定 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);
                }
              }
            
            

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

            【讨论】:

            • 从 1.12.1 版本开始,validateTLSCertificates 方法已被正式删除。 (见jsoup.org/news/release-1.12.1
            • @Stephan - 谢谢!从答案中删除不再相关的那部分。
            • @Kumar 有一些具体的错误要分享?问题是什么?在我的 Kotlin 代码 atm 的某处使用相同的解决方案,效果很好。
            • 这在 mori 的答案有效时不起作用。这与 URL 中的非标准端口有关吗? IU 正在使用“192.168.1.10:5001”。
            • @eos1d3 是否返回了一些特定的错误?一般来说 - 没有足够的信息来得出任何结论。解决方案是有效的,感觉就像你有一些极端情况。
            【解决方案10】:

            在此处测试解决方案后。奇怪的是,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 测试的。

            【讨论】:

              猜你喜欢
              • 2013-04-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多