带有 Jersey for SSL 的 ApacheConnectorProvider 抛出错误

ApacheConnectorProvider with Jersey for SSL throws Error

我正在尝试 post 使用 SSL URL 的服务调用。我遇到异常。

javax.ws.rs.ProcessingException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at org.glassfish.jersey.apache.connector.ApacheConnector.apply(ApacheConnector.java:517)
    at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:246)
    at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:667)
    at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:664)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315).

我正在使用 Spring,Jersey 并尝试使用 ApacheClient(使用原因,在 Weblogic 上它采用 Weblogic 特定的 HTTP 处理程序,我知道我们可以使用 "DUseSunHttpHandler=true" 但我不想在生产中这样做)。

请注意,仅通过 Jersey 实现,它可以在 Tomcat 和 Weblogic 上使用 http。但是对于 HTTPS,它在 Tomcat 中工作,而不是在 Weblogic 中工作。所以去了 ApacheConnectionProvider,从那里它也不能在 Tomcat 中工作。

POM 条目

<dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-client</artifactId>
        <version>2.15</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-common</artifactId>
        <version>2.15</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>javax.ws.rs</groupId>
        <artifactId>javax.ws.rs-api</artifactId>
        <version>2.0.1</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.4</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.connectors</groupId>
        <artifactId>jersey-apache-connector</artifactId>
        <version>2.15</version>
    </dependency>

代码

public Client getClient() {
        ClientConfig clientConfig = new ClientConfig();
        clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, new PoolingHttpClientConnectionManager());
        //config your ssl for apache connector
        SslConfigurator sslConfig = SslConfigurator.newInstance();

        String trustStoreFile = "C:\Development\svn\ecomm-webapp\profiles\uat\workflowtrust.jks";
        String trustStorePassword ="ABC12";
        String keyStoreFile = "C:\Development\svn\ecomm-webapp\profiles\uat\workflow.jks";
        String keyPassword ="abc12";
        sslConfig.trustStoreFile(trustStoreFile).keyStoreFile(keyStoreFile).keyStorePassword(trustStorePassword).trustStorePassword(trustStorePassword).securityProtocol("SSL");
        clientConfig.property(ApacheClientProperties.SSL_CONFIG, sslConfig);
        ApacheConnectorProvider connector = new ApacheConnectorProvider();
        clientConfig.connectorProvider(connector);

        return  ClientBuilder.newClient(clientConfig);
    }

调用代码

public <T> T postXmlFile(final File inputXml, final String targetUrl, Class<T> resultResponse, final RestfulServiceVerifier<T> restfulServiceVerifier) throws FunctionalException {
        return post(MediaType.APPLICATION_XML_TYPE, MediaType.TEXT_XML_TYPE, inputXml, targetUrl, resultResponse, restfulServiceVerifier);
    }

    private <T> T post(final MediaType type, final MediaType accept, final Object entity, final String targetUrl, Class<T> resultResponse, final RestfulServiceVerifier<T> restfulServiceVerifier) throws FunctionalException {
        Response response = null;
        int responseStatus = -1;
        T result = null;
        while (restfulServiceVerifier.hasNextWorkFlowHit()) {
            try {
                response = restFulWebTargetFactory.getClient().target(targetUrl)
                        .request(type)
                        .post(Entity.entity(entity, accept));

                responseStatus = response.getStatus();
                if(EcommConstants.WORKFLOW_ORDER_PROPOSAL_SUCCESS_CODE == responseStatus) {
                    final String resultInString =  response.readEntity(String.class);
                    //for audit purpose
                    if (LOG_XML_MESSAGE) {
                        log.info("XML Response "+ resultInString);
                    }
                    result = unMarshalXML(resultInString, resultResponse);
                    restfulServiceVerifier.checkResponse(result, responseStatus);
                }

            } catch (WorkFlowRetryException workFlowException) {
                throw new FunctionalException("WebService post failed. ", workFlowException);
            }catch (WorkFlowValidationException workFlowValidationException) {
                log.error("Service Response Validation Exception " + workFlowValidationException.getErrorCode() + " Error Description " + workFlowValidationException.getDescription(), workFlowValidationException);
            }catch (final Exception e) {
                log.error("Exception occurred" , e);
            } finally {
                if(null != response) {
                    response.close();
                }
            }
        }
        if(-1 == responseStatus) {
            throw new FunctionalException("WebService post failed. ", new Exception());
        }
        return result;
    }

我修复了,请找到解决方案。我更改了 getClient() 方法。

  1. 我创建了 org.apache.http.conn.ssl.DefaultHostnameVerifier。这是我 changed.DefaultHostnameVerifier 的代码片段(我只需要默认的)
  2. 使用信任库和密钥配置文件配置 SslConfigurator
  3. 创建 LayeredConnectionSocketFactory 并从 SslConfigurator 和 defaultHostnameVerifier 注入 SSLContext
  4. 为 http 和 https 创建了注册表
  5. 创建 ClientConfig 以使用注入注册表的 PoolingHttpClientConnectionManager 设置连接管理器
  6. 并将 SSL 配置设置为客户端配置
  7. 已创建 ApacheConnectorProvider 并设置为 clientConfig

代码示例

private SslConfigurator createSSLContext() {
        return SslConfigurator.newInstance()
                .trustStoreFile(trustStoreFile)
                .trustStorePassword(trustStorePassword)
                .keyStoreFile(keyStoreFile)
                .keyPassword(keyPassword).securityProtocol("SSL");
    }


    @Override
    public Client getClient() {

        HostnameVerifier defaultHostnameVerifier=new DefaultHostnameVerifier();
        SslConfigurator sslConfig = createSSLContext();
        LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
                sslConfig.createSSLContext(),
                defaultHostnameVerifier);

        final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        ClientConfig clientConfig = new ClientConfig();
        clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, new PoolingHttpClientConnectionManager(registry));

        clientConfig.property(ApacheClientProperties.SSL_CONFIG, sslConfig);

        ApacheConnectorProvider connector = new ApacheConnectorProvider();
        clientConfig.connectorProvider(connector);
        return ClientBuilder.newBuilder().withConfig(clientConfig).build();
    }