【问题标题】:With https URL, HttpsURLConnection works but HttpClient does not使用 https URL,HttpsURLConnection 有效,但 HttpClient 无效
【发布时间】:2016-08-11 19:31:35
【问题描述】:

我有一个 Web 应用程序(在 WebSphere 上),它与第 3 方交互以格式化一些接收到的数据,然后对位于 Tomcat server 上的所述第 3 方进行 RESTful 调用。在 Web 应用程序中,我可以使用 Java 的本机 HttpsUrlConnection(见下文)成功地进行基本的 RESTful GET 调用,但是当我使用 Apache 的 HttpClient 时,它会因 SSL 链接错误而失败。

HttpsURLConnection:

    String urlWithParams = "https://example.com/rest/issue/59"; 

    String methodType = "GET"; 
    String acceptType = MediaType.APPLICATION_JSON;

    HttpsURLConnection conn = null; 

    try {

        URL url = new URL(urlWithParams);
        conn = (HttpsURLConnection) url.openConnection();
        conn.setRequestMethod(methodType);
        conn.setRequestProperty("Accept", acceptType);

        if (conn.getResponseCode() != 200) {
            System.out.println("Failed. httpErrorUrl=" + conn.getResponseCode()
                    + " httpErrorCode=" + conn.getResponseCode()
                    + " httpErrorMsg=" + conn.getResponseMessage());

        }

        BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));

        String rawData;
        String toReturn = "";
        while ((rawData = br.readLine()) != null) {
            toReturn += rawData;
        }

        System.out.println(toReturn);

    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally{
        if(conn != null){
            conn.disconnect();
        } else {
            System.out.println("httpUrlConnection is null");
        }
    }

HttpClient 代码:

    String uri = "https://example.com/rest/issue/59"; 
    PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
    connManager.setDefaultMaxPerRoute(20);
    connManager.setMaxTotal(40);

    CloseableHttpClient httpclient = HttpClients.createDefault();

HttpClient 方法在下面的函数中执行:

    private JSON request(HttpRequestBase req) throws RestException, IOException {
        req.addHeader("Accept", "application/json");

        if (creds != null)
            creds.authenticate(req);

        HttpResponse resp = httpClient.execute(req);
        HttpEntity ent = resp.getEntity();
        StringBuilder result = new StringBuilder();

        if (ent != null) {
            String encoding = null;
            if (ent.getContentEncoding() != null) {
                encoding = ent.getContentEncoding().getValue();
            }

            if (encoding == null) {
                Header contentTypeHeader = resp.getFirstHeader("Content-Type");
                HeaderElement[] contentTypeElements = contentTypeHeader.getElements();
                for (HeaderElement he : contentTypeElements) {
                    NameValuePair nvp = he.getParameterByName("charset");
                    if (nvp != null) {
                        encoding = nvp.getValue();
                    }
                }
            }

            InputStreamReader isr =  encoding != null ?
                new InputStreamReader(ent.getContent(), encoding) :
                new InputStreamReader(ent.getContent());
            BufferedReader br = new BufferedReader(isr);
            String line = "";

            while ((line = br.readLine()) != null)
                result.append(line);
        }

        StatusLine sl = resp.getStatusLine();

        if (sl.getStatusCode() >= 300)
            throw new RestException(sl.getReasonPhrase(), sl.getStatusCode(), result.toString());

        return result.length() > 0 ? JSONSerializer.toJSON(result.toString()): null;
    }    

产生的错误是典型的链接异常,这通常意味着SSL 证书不正确。由于证书是在应用服务器级别导入的,我希望SSL 连接对于HttpsURLConnectionHttpClient 调用的处理方式相同。

Caused by: `com.ibm.jsse2.util.j: PKIX` path building failed: `java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl` co

无法构建有效的CertPath.; 内部原因是: java.security.cert.CertPathValidatorException: CN=Entrust Root Certification Authority 颁发的证书,OU="(c) 2 006 Entrust, Inc.", OU=www.entrust.net/CPS 通过引用并入,O="Entrust, Inc.", C=US 不可信;内部原因是:

    `java.security.cert.CertPathValidatorException:` Certificate chaining error
    at com.ibm.jsse2.util.h.b(h.java:18) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.util.h.b(h.java:118) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.util.g.a(g.java:14) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.pc.a(pc.java:41) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.pc.checkServerTrusted(pc.java:1) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.pc.b(pc.java:90) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.lb.a(lb.java:499) ~[na:6.0 build_20141024]
    ... 80 common frames omitted

原因:java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl 无法构建有效的 CertPath。 在com.ibm.security.cert.PKIXCertPathBuilderImpl.engineBuild(PKIXCertPathBuilderImpl.java:411) ~[na:na] 在java.security.cert.CertPathBuilder.build(CertPathBuilder.java:258) ~[na:na] com.ibm.jsse2.util.h.b(h.java:61) ~[na:6.0 build_20141024] ... 86 常用框架省略

为什么HttpsURLConnection 申请了证书,HttpClient 却没有?我究竟做错了什么?

【问题讨论】:

标签: java ssl apache-httpclient-4.x httpsurlconnection


【解决方案1】:

以下问题有一个很好的代码示例,说明如何通过 HttpClient 使用 HTTPS。

Using Apache httpclient for https

简而言之:您的代码缺少所有 HTTPS 特定设置。您正在使用它,就像它只是一个常规 HTTP 请求一样。

【讨论】:

  • 我不确定我明白你在说什么。在HttpClient SSL Docs 中,它指出正确安装 JSSE 后,通过 SSL 进行的安全 HTTP 通信应该与普通 HTTP 通信一样简单。由于我的调用源在 WebSphere 中,它应该处理所有 SSL 协商(就像我使用 HttpsURLConnection 时一样,对吧?
  • 现在看来不是这样。尝试链接帖子中给出的示例,看看它是否有效。
【解决方案2】:

HttpClient 不考虑系统属性,除非明确配置为做。

尝试替换

HttpClients.createDefault()

HttpClients.createSystem()

另请注意,您的代码中没有使用PoolingHttpClientConnectionManager 实例。除非您有充分的理由,否则不要创建自定义连接管理器。

【讨论】:

  • 谢谢@oleg。哇,我无法相信在我在 Apache HttpClient 和 SSL 上进行的所有谷歌搜索之后,我没有遇到 HttpClients.createSystem()。非常感谢您提供的信息,它解决了我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-15
  • 2013-05-05
  • 1970-01-01
  • 2016-03-30
  • 1970-01-01
  • 1970-01-01
  • 2017-05-08
相关资源
最近更新 更多