zoukankan      html  css  js  c++  java
  • HttpClient实现https调用

    • 在HttpClient 4.x版本中引入了大量的构造器设计模式

    • https请求建立详解

    首先建立一个信任任何密钥的策略。代码很简单,不去考虑证书链和授权类型,均认为是受信任的:

    
    class AnyTrustStrategy implements TrustStrategy{
    
    @Override
    
    public boolean isTrusted(X509Certificate[] chain, String authType)throws CertificateException {
    
    return true;
    
    }
    
    }
    
    

    HttpClient既能处理常规http协议,又能支持https,根源在于在连接管理器中注册了不同的连接创建工厂。当访问url的schema为http时,调用明文连接套节工厂来建立连接;当访问url的schema为https时,调用SSL连接套接字工厂来建立连接。对于http的连接我们不做修改,只针对使用SSL的https连接来进行自定义:

    RegistryBuilder registryBuilder = RegistryBuilder.create();
    
    ConnectionSocketFactory plainSF =newPlainConnectionSocketFactory();
    
    registryBuilder.register("http", plainSF);
    
    //指定信任密钥存储对象和连接套接字工厂
    
    try{
    
    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    
    SSLContext sslContext = SSLContexts.custom().useTLS().loadTrustMaterial(trustStore,newAnyTrustStrategy()).build();
    
    LayeredConnectionSocketFactory sslSF =newSSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    
    registryBuilder.register("https", sslSF);
    
    }catch(KeyStoreException e) {
    
    thrownewRuntimeException(e);
    
    }catch(KeyManagementException e) {
    
    thrownewRuntimeException(e);
    
    }catch(NoSuchAlgorithmException e) {
    
    thrownewRuntimeException(e);
    
    }
    
    Registry registry = registryBuilder.build();
    

    在上述代码中可以看到,首先建立了一个密钥存储容器,随后让SSLContext开启TLS,并将密钥存储容器和信任任何主机的策略加载到该上下文中。构造SSL连接工厂时,将自定义的上下文和允许任何主机名通过校验的指令一并传入。最后将这样一个自定义的SSL连接工厂注册到https协议上。

    //设置连接管理器
    
    PoolingHttpClientConnectionManager connManager =newPoolingHttpClientConnectionManager(registry);
    
    connManager.setDefaultConnectionConfig(connConfig);
    
    connManager.setDefaultSocketConfig(socketConfig);
    
    //构建客户端
    
    HttpClient client= HttpClientBuilder.create().setConnectionManager(connManager).build();
    

    为了让我们的HttpClient具有多线程处理的能力,连接管理器选用了PoolingHttpClientConnectionManager,将协议注册信息传入连接管理器,最后再次利用构造器的模式创建出我们需要的HttpClient。随后的GET/POST请求发起方法http和https之间没有差异。
    为了验证我们的代码是否成功,可以做下JUnit单元测试:

    @Test
    
    publicvoiddoTest()throwsClientProtocolException, URISyntaxException, IOException{
    
    HttpUtil util = HttpUtil.getInstance();
    
    InputStream in = util.doGet("https://kyfw.12306.cn/otn/leftTicket/init");
    
    String retVal = HttpUtil.readStream(in, HttpUtil.defaultEncoding);
    
    System.out.println(retVal);
    
    }
    

    执行后可以在控制台看到12306余票查询界面的html代码

    • 有朋友反馈说提供的工具类中没有直接POST JSON对象的方法,下面我提供一下基础方法,供参考(此代码未包含在下文的共享资源中,请自行补充进去)
    /**
    
    * 基本Post请求
    
    * @param url 请求url
    
    * @param queryParams 请求头的查询参数
    
    * @param json 直接放入post请求体中的文本(请使用JSON)
    
    * @return
    
    * @throws URISyntaxException
    
    * @throws UnsupportedEncodingException
    
    */
    
    publicHttpResponse doPostBasic(String url, Map queryParams, String json)throwsURISyntaxException, ClientProtocolException, IOException{
    
    HttpPost pm =newHttpPost();
    
    URIBuilder builder =newURIBuilder(url);
    
    //填入查询参数
    
    if(MapUtils.isNotEmpty(queryParams)){
    
    builder.setParameters(HttpUtil.paramsConverter(queryParams));
    
    }
    
    pm.setURI(builder.build());
    
    //填入post json数据
    
    if(StringUtils.isNotBlank(json)){
    
    //下面的ContentType完整类名为:org.apache.http.entity.ContentType
    
    pm.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
    
    }
    
    return client.execute(pm);
    
    }
    

    • 下面再列举一个传入指定秘钥的https访问方法
    public void initSSLConfigForTwoWay() throws Exception {
    
            HttpClientBuilder b = HttpClientBuilder.create();
            // 1 Import your own certificate
    //		String demo_base_Path = System.getProperty("user.dir");
    //		String demo_base_Path = getClass().getClassLoader().getResource("").getPath();
    //		String selfcertpath = demo_base_Path +  Constant.SELFCERTPATH;
    //		String trustcapath = demo_base_Path +  Constant.TRUSTCAPATH;
    
    //		String selfcertpath = demo_base_Path + Constant.SELFCERTPATH;
    //		String trustcapath = demo_base_Path + Constant.TRUSTCAPATH;
    
            KeyStore selfCert = KeyStore.getInstance("pkcs12");
            selfCert.load(getClass().getClassLoader().getResourceAsStream(NbConstant.CERT_FLODER + File.separatorChar + NbConstant.SELFCERTPATH),
                    NbConstant.SELFCERTPWD.toCharArray());
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
            kmf.init(selfCert, NbConstant.SELFCERTPWD.toCharArray());
    
            // 2 Import the CA certificate of the server,
            KeyStore caCert = KeyStore.getInstance("jks");
            caCert.load(getClass().getClassLoader().getResourceAsStream(NbConstant.CERT_FLODER + File.separatorChar + NbConstant.TRUSTCAPATH), NbConstant.TRUSTCAPWD.toCharArray());
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
            tmf.init(caCert);
    
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    
            // 3 Set the domain name to not verify
            // (Non-commercial IoT platform, no use domain name access generally.)
            SSLSocketFactory ssf = new SSLSocketFactory(sc, new String[]{"TLSv1.2", "TLSv1.1", "TLSv1"}, null,
                    SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    
            // If the platform has already applied for a domain name which matches
            // the domain name in the certificate information, the certificate
            // domain name check can be enabled (open by default)
            // SSLSocketFactory ssf = new SSLSocketFactory(sc);
    
    //		ClientConnectionManager ccm = this.getConnectionManager();
    //		SchemeRegistry sr = ccm.getSchemeRegistry();
    //		sr.register(new Scheme("https", 8743, ssf));
    
    
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.getSocketFactory())
                    .register("https", ssf)
                    .build();
    
            //      -- allows multi-threaded use
            PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            connMgr.setMaxTotal(200);
            connMgr.setDefaultMaxPerRoute(100);
            b.setConnectionManager(connMgr);
    
            // finally, build the HttpClient;
            //      -- done!
    
    
    //		httpClient = new DefaultHttpClient(ccm);
            httpClient = b.build();
        }
    
    定位问题原因* 根据原因思考问题解决方案* 实践验证方案有效性* 提交验证结果
  • 相关阅读:
    Django contenttypes 组件
    CPU 的工作原理
    Redis基础
    面向对象中的__slots__
    DRF 的解析器和渲染器
    DRF的认证、权限 和 限制
    DRF中的版本控制
    REST framework 视图
    Java多线程(1):3种常用的实现多线程类的方法
    Java多线程(2):线程加入/join()
  • 原文地址:https://www.cnblogs.com/jimoliunian/p/12965366.html
Copyright © 2011-2022 走看看