Jersey 2 - 带有 .target HTTPS

Posted

技术标签:

【中文标题】Jersey 2 - 带有 .target HTTPS【英文标题】:Jersey 2 - with .target HTTPS 【发布时间】:2018-09-27 23:44:37 【问题描述】:

我一直在尝试从 jersey 客户端执行 POST,但继续遇到以下异常:

原因:java.lang.IllegalStateException:已连接 在 sun.net.www.protocol.http.HttpURLConnection.setRequestProperty(HttpURLConnection.java:3071) 在 sun.net.www.protocol.https.HttpsURLConnectionImpl.setRequestProperty(HttpsURLConnectionImpl.java:325) 在 org.glassfish.jersey.client.internal.HttpUrlConnector.setOutboundHeaders(HttpUrlConnector.java:424) 在 org.glassfish.jersey.client.internal.HttpUrlConnector.lambda$_apply$0(HttpUrlConnector.java:381) 在 org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:195) 在 org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:189) 在 org.glassfish.jersey.message.internal.CommittingOutputStream.commit(CommittingOutputStream.java:257) 在 org.glassfish.jersey.message.internal.OutboundMessageContext.commitStream(OutboundMessageContext.java:825) 在 org.glassfish.jersey.client.ClientRequest.doWriteEntity(ClientRequest.java:553) 在 org.glassfish.jersey.client.ClientRequest.writeEntity(ClientRequest.java:498) 在 org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:384) 在 org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:282) 在 org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:278) ... 63 更多

我在 SO 中尝试了很多答案和解决方案,但都没有成功。

这里发疯了,请帮忙!

public class JerseyWithSSL 

    public static javax.ws.rs.client.Client getRESTClient() 

        try 
            TrustManager[] trustMgr = new TrustManager[]new X509TrustManager() 
                public X509Certificate[] getAcceptedIssuers() 
                    return null;
                

                public void checkClientTrusted1(X509Certificate[] certs,
                        String authType) 
                

                public void checkServerTrusted1(X509Certificate[] certs,
                        String authType) 
                

                @Override
                public void checkClientTrusted(X509Certificate[] arg0, String arg1)
                        throws CertificateException 
                    // TODO Auto-generated method stub

                

                @Override
                public void checkServerTrusted(X509Certificate[] arg0, String arg1)
                        throws CertificateException 
                    // TODO Auto-generated method stub

                
            ;

            SSLContext context = SSLContext.getInstance("SSL");
            context.init(null, trustMgr, null);

            javax.ws.rs.client.Client client = ClientBuilder.newBuilder().sslContext(context)
                    .hostnameVerifier(new HostnameVerifier() 

                        @Override
                        public boolean verify(String arg0, SSLSession arg1) 
                            // TODO Auto-generated method stub
                            return true;
                        
                    ).build();

            return client;
         catch (NoSuchAlgorithmException | KeyManagementException ex) 
            Logger.getLogger(JerseyWithSSL.class.getName()).log(Level.SEVERE, null, ex);
        

        return null;
    

使用上述类的代码(此处抛出异常):

    // send notification to all subscriptors for event: attendee.create
    if (!subscriptions.isEmpty()) 

        try 
            Client client = JerseyWithSSL.getRESTClient();

            for (Subscription sub : subscriptions) 

                System.out.println("Subscription: \n" + sub.getEventName() + "\n" + sub.getTargetUrl());

                Response resp = client.target(sub.getTargetUrl())
                        .request(MediaType.APPLICATION_JSON)
                        .post(Entity.entity(newAttdee, MediaType.APPLICATION_JSON));

                System.out.println("Status: " + resp.getStatus());
                System.out.println(resp.getEntity().toString());

                resp.close();
            
         catch (Exception ex) 
            Logger.getLogger(AttendeeResource.class.getName()).log(Level.SEVERE, null, ex);
        

    

添加 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ON24Hooks</groupId>
    <artifactId>ON24_Zapier_Hooks</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>ON24_Zapier_Hooks</name>

    <properties>
        <endorsed.dir>$project.build.directory/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.connectors</groupId>
            <artifactId>jersey-apache-connector</artifactId>
            <version>2.16</version>
            <type>jar</type>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>$endorsed.dir</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>$endorsed.dir</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

【问题讨论】:

第一次调用本身会发生这种情况吗?错误似乎重用客户端对象可能是? 嗨@TarunLalwani,确实客户端在for循环中使用。更新代码... 你能把Client client = JerseyWithSSL.getRESTClient();移到循环里面看看有没有帮助? 这有什么更新吗?我假设在循环中移动客户端创建应该为您解决问题 你能不能也分享你的pom.xml 我在resp 上没有看到close 方法 【参考方案1】:

IllegalStateException: Already connected 可能掩盖了 SSLHandshakeException,如问题 #3000 中所述(以前称为 JERSEY-2728)。

根据release notes,它已在 Jersey 2.23 中修复。不过,我会将泽西岛升级到最新版本。

【讨论】:

我添加了 jersey-client:2.27 和 jersey-apache-connector:2.27 --- 最新版本,问题仍然存在。【参考方案2】:

我花了一些时间弄清楚它是如何在 Jersey 2.x 中完成的。感谢Jersey Migration Guide 帮助我。 下面是如何在 Jersey 2.x 中为 HTTPS 调用构建客户端的代码 sn-p

 public Client getRESTClient() 

      TrustManager[] trustMgr= new TrustManager[]  new X509TrustManager() 
       public X509Certificate[] getAcceptedIssuers() 
        return null;
       

       public void checkClientTrusted1(X509Certificate[] certs,
         String authType) 
       

       public void checkServerTrusted1(X509Certificate[] certs,
         String authType) 
       

       @Override
       public void checkClientTrusted(X509Certificate[] arg0, String arg1)
         throws CertificateException 
        // TODO Auto-generated method stub

       

       @Override
       public void checkServerTrusted(X509Certificate[] arg0, String arg1)
         throws CertificateException 
        // TODO Auto-generated method stub

       
       ;

      SSLContext context = SSLContext.getInstance("TLS");
      context.init(null, trustMgr, null);


      Client client = ClientBuilder.newBuilder().sslContext(context)
        .hostnameVerifier(new HostnameVerifier() 

         @Override
         public boolean verify(String arg0, SSLSession arg1) 
          // TODO Auto-generated method stub
          return true;
         
        ).build();

      return client;

     

任何时候都可能发生异常,也可能是 SSL 证书验证,也可以是当前套接字层连接来验证 SSLContext。请彻底调试 try catch 块,也使您的 http 请求与其他客户端的执行也一样来自Java Url URLCONNECTION

【讨论】:

【参考方案3】:

IllegalStateException: 已连接可能正在掩盖 SSLHandshakeException。验证/调试的方法是使用以下 JVM 标志:

-Djavax.net.debug=ssl:handshake:verbose:keymanager:trustmanager -Djava.security.debug=access:stack

这将导致 JVM 打印出一大堆有关 SSL 的调试信息,如果有 SSL 异常,则会显示该异常。

【讨论】:

以上是关于Jersey 2 - 带有 .target HTTPS的主要内容,如果未能解决你的问题,请参考以下文章

带有 Jersey 2.2 和 Jackson 2.1 的自定义 ObjectMapper

带有 Jersey 客户端版本 2.2 的 Restful WebService 调用

JAX-RS(Jersey 2 实现)内容协商,带有 URL 扩展名 .xml 或 .json

RxJava jersey客户端,带有使用WebResourceFactory构建的代理

使用 JAX-RS Jersey 2.2 获取带有 Content-Type 和 Accept 标头的请求

返回带有 Jersey 的字符串的 JSON 表示