通过 Jersey 发送请求时出现 javax.ws.rs.ProcessingException

Ram*_*kar 3 java rest web-services jersey

我正在尝试使用密钥库文件将 JSON 请求发送到安全的 REST web 服务,并且我正在使用 Jersey API。下面是我的代码片段

SslConfigurator sslConfigurator = SslConfigurator.newInstance().trustStoreFile("C:\\Users\\******\\test.keystore").trustStorePassword("password");
SSLContext sslContext = sslConfigurator.createSSLContext();
Client client = ClientBuilder.newBuilder().sslContext(sslContext).build();
WebTarget target = client.target("https://hostname:portnumber").path("resourse/methodname/v1");

Form form = new Form();
form.param("key1", "value1");
form.param("key2", "value2");

Response response = target.request(MediaType.APPLICATION_JSON_TYPE).post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
System.out.println(response);
Run Code Online (Sandbox Code Playgroud)

但是我在最后一行出现以下异常。

Exception in thread "main" javax.ws.rs.ProcessingException: Already connected
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:264)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:684)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:681)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:681)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:437)
at org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:343)
at TestMain.main(TestMain.java:29)
Caused by: java.lang.IllegalStateException: Already connected
at sun.net.www.protocol.http.HttpURLConnection.setRequestProperty(HttpURLConnection.java:3014)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.setRequestProperty(HttpsURLConnectionImpl.java:316)
at org.glassfish.jersey.client.internal.HttpUrlConnector.setOutboundHeaders(HttpUrlConnector.java:421)
at org.glassfish.jersey.client.internal.HttpUrlConnector.access$100(HttpUrlConnector.java:96)
at org.glassfish.jersey.client.internal.HttpUrlConnector$4.getOutputStream(HttpUrlConnector.java:384)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:200)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:194)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commit(CommittingOutputStream.java:262)
at org.glassfish.jersey.message.internal.OutboundMessageContext.commitStream(OutboundMessageContext.java:816)
at org.glassfish.jersey.client.ClientRequest.writeEntity(ClientRequest.java:545)
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:388)
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:285)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:255)
... 10 more
Run Code Online (Sandbox Code Playgroud)

Kin*_*ung 6

其实是jersey的一个bug,异常中的错误信息基本是错误的。请参阅SSLHandshakeException 被无用的 IllegalStateException 屏蔽:已连接

所以基本上,这意味着服务器和客户端之间的 SSL 握手存在问题。

异常的原因之一与此有关:Jersey API Doc - 5.9。保护客户

... ClientBuilder 还提供了一种用于定义自定义 HostnameVerifier 实现的方法。当默认主机 URL 验证失败时,将调用 HostnameVerifier 实现。

重要的

HostnameVerifier 的行为取决于 http 客户端实现。HttpUrlConnector 和 ApacheConnector 工作正常,这意味着在 URL 验证失败后 HostnameVerifier 被调用,并且可以使用 HostnameVerifier 的自定义实现重新验证 URL 并继续进行手动处理。JettyConnector 和 GrizzlyConnector 仅提供主机 URL 验证并抛出 CertificateException,而无法使用自定义 HostnameVerifier。此外,在 JettyConnector 的情况下,有一个属性 JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION 可以在握手中禁用整个主机 URL 验证机制。

如果您使用的是 Grizzly,则无法将其关闭,但有一个解决方法。就是在客户端配置中设置一个自定义的HostnameVerifier,verify()总是返回true:

        Client c = ClientBuilder.newBuilder().sslContext(sslContext).hostnameVerifier(new HostnameVerifier(){
              @Override
              public boolean verify(String paramString, SSLSession paramSSLSession) {
               return true;
             }
        }).build();
Run Code Online (Sandbox Code Playgroud)