美文网首页技术干货
java 利用 HttpClient 进行 HTTPS 请求

java 利用 HttpClient 进行 HTTPS 请求

作者: Dandelion丶 | 来源:发表于2017-09-20 09:47 被阅读0次

    直接使用 HttpClient 进行 https 请求,会由于证书问题导致请求失败,既然我们想利用程序访问某个网站(比如做爬虫),其实我们对证书并不关注,可以采用忽略证书校验的方式来实现对 https 请求的访问。

    具体实现流程

    1. 依赖的 HttpClient jar 包版本

      <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpclient</artifactId>
          <version>4.5.3</version>
      </dependency>
      
    2. 封装获取 HttpClient 的方法

    public static CloseableHttpClient getHttpClient() {
            try {
                SSLContextBuilder builder = new SSLContextBuilder();
                builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
                //不进行主机名验证
                SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(builder.build(),
                        NoopHostnameVerifier.INSTANCE);
                Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
                        .register("http", new PlainConnectionSocketFactory())
                        .register("https", sslConnectionSocketFactory)
                        .build();
    
                PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
                cm.setMaxTotal(100);
                CloseableHttpClient httpclient = HttpClients.custom()
                        .setSSLSocketFactory(sslConnectionSocketFactory)
                        .setDefaultCookieStore(new BasicCookieStore())
                        .setConnectionManager(cm).build();
                return httpclient;
            } catch (KeyManagementException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (KeyStoreException e) {
                e.printStackTrace();
            }
            return HttpClients.createDefault();
        }
    
    1. 创建一个利用 HttpClient 发送 get 请求的工具方法
    public static String get(String uri, Map<String, Object> params, Map<String, String> headers) throws IOException {
            HttpClient client = getHttpClient();
            return get(client, uri, params, headers);
        }
    
        public static String get(HttpClient client, String uri, Map<String, Object> params, Map<String, String> headers) throws IOException {
            String result = StringUtils.EMPTY;
            String fullUrl = buildUrlWithParams(uri, params);
            HttpGet httpGet = new HttpGet(fullUrl);
    
            if (headers != null) {
                for (Entry<String, String> entry : headers.entrySet()) {
                    httpGet.addHeader(entry.getKey(), entry.getValue());
                }
            }
            HttpResponse httpResponse = client.execute(httpGet);
            InputStream input = httpResponse.getEntity().getContent();
            if (null != input) {
                try {
                    result = IOUtils.toString(input, "UTF-8");
                } catch (IOException e) {
                    throw e;
                } finally {
                    IOUtils.closeQuietly(input);
                }
            }
            return result;
        }
    
    
        private static String buildUrlWithParams(String uri, Map<String, Object> params) throws UnsupportedEncodingException {
            StringBuilder urlBuilder = new StringBuilder(uri);
            if (null != params && !params.isEmpty()) {
                if (!uri.contains("?")) {
                    urlBuilder.append("?");
                }
                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    String key = entry.getKey();
                    Object value = entry.getValue();
                    String valueStr = null == value ? "" : value.toString();
                    if (!urlBuilder.toString().endsWith("?")) {
                        urlBuilder.append("&");
                    }
                    urlBuilder.append(key).append("=").append(URLEncoder.encode(valueStr, "utf-8"));
                }
            }
            String fullUrl = urlBuilder.toString();
            return fullUrl;
        }
    
    1. 测试代码
    public class HttpsReqTest extends TestCase {
    
        public void reqHttpsUrlTest() throws IOException {
            String url = "https://www.xxx.com/";
            String result = get(url, null, null);
            System.out.println(result);
        }
    }
    

    问题总结

    上面这段代码其实已经可以满足大部分 https 地址的请求,本来我也以为高枕无忧了,但是昨天我请求一个 https 地址的时候(地址就不暴露了),又发现了新的问题,刚好补充下,后续有其他问题也会继续补充。

    问题一

    • 问题描述
    Exception in thread "main" javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
        at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1904)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:279)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:273)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1446)
        at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:209)
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:901)
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:837)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1023)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1332)
        at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1359)
        at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1343)
        at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
        at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355)
        at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:359)
        at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:381)
        at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
        at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
        at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
        at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111)
        at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
    Caused by: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
        at sun.security.ssl.AbstractTrustManagerWrapper.checkAlgorithmConstraints(SSLContextImpl.java:1018)
        at sun.security.ssl.AbstractTrustManagerWrapper.checkAdditionalTrust(SSLContextImpl.java:944)
        at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:886)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1428)
        ... 26 more
    
    • 问题原因

      原因是 Java 7/8 版本增加了安全性

    • 解决方案

    把 %JAVA_HOME%/jre/lib/security/java.security 文件里的 
    
    jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 1024 
    
    改成
    
    jdk.certpath.disabledAlgorithms=
    
    就是去掉 "MD2, DSA, RSA keySize < 1024"。
    

    用 notepad++ 修改的时候会提示文件被占用,可以用管理员运行 notepad++ 在进行修改即可。

    相关文章

      网友评论

        本文标题:java 利用 HttpClient 进行 HTTPS 请求

        本文链接:https://www.haomeiwen.com/subject/ycmnsxtx.html