简体   繁体   English

带有附件的Web服务在双向SSL连接上失败

[英]Web-service with attachment failed on 2-ways SSL connection

I have two applications, one that provides a Web-service (let's call it ws-provider ), the other one that is the client of this Web-Service (call it ws-client ). 我有两个应用程序,一个提供Web服务(让我们称之为ws-provider ),另一个是Web服务ws-client (称之为ws-client )。 This communication is protected through a 2-ways SSL. 此通信通过双向SSL进行保护。 Both servers have been correctly configured regarding this confidentiality restrictions (certificates installation, SSL configuration, Tomcat parametrization...) 两个服务器都已针对此机密性限制进行了正确配置(证书安装,SSL配置,Tomcat参数化......)

The servers are running on Tomcat (5.5 for ws-provider , 6 for ws-client ), Java 6 and uses Jax-WS for providing/consuming the web-service. 服务器在Tomcat上运行( ws-provider为5.5, ws-client为6),Java 6,并使用Jax-WS提供/使用Web服务。

When the user connects to the ws-client application and executes the action that calls the ws-provider web-service, the latter action generally fails with the following error: 当用户连接到ws-client应用程序并执行调用ws-provider Web服务的操作时,后一操作通常会失败,并显示以下错误:

com.sun.xml.ws.client.ClientTransportException: The server sent HTTP status code 400: No client certificate chain in this request
        at com.sun.xml.ws.transport.http.client.HttpClientTransport.checkResponseCode(HttpClientTransport.java:218)
        at com.sun.xml.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:137)
        at com.sun.xml.ws.transport.http.client.HttpTransportPipe.processRequest(HttpTransportPipe.java:74)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:559)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:518)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:503)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:400)
        at com.sun.xml.ws.client.Stub.process(Stub.java:234)
        at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:120)
        at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:230)
        at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:210)
        at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:103)
        at $Proxy33.createOrRenewRequest(Unknown Source)
        at my.app.MyPushRequest.sendXMLRequest(MyPushRequest.java:29)
        at my.app.MyRequestCreation.sendRequestForDraftApproval(MyRequestCreation.java:284)

On the ws-provider , I get this error: ws-provider ,我收到此错误:

WARNING: Exception getting SSL attributes
java.net.SocketException: SSL Cert handshake timeout
        at org.apache.tomcat.util.net.jsse.JSSE14Support.synchronousHandshake(JSSE14Support.java:101)
        at org.apache.tomcat.util.net.jsse.JSSE14Support.handShake(JSSE14Support.java:67)
        at org.apache.tomcat.util.net.jsse.JSSESupport.getPeerCertificateChain(JSSESupport.java:121)
        at org.apache.coyote.http11.Http11Processor.action(Http11Processor.java:1127)
        at org.apache.coyote.Request.action(Request.java:349)
        ...

Now, the strange part is that I just said that it generally failed when calling the web-service. 现在,奇怪的是我只是说它在调用Web服务时通常会失败。 For some reasons, sometimes I get a successfull attempt when calling the web-service. 出于某些原因,有时我在调用Web服务时会成功尝试。 This generally happens if the user launches the action in the minute that follows his login to the application (No, I don't know why!)... 如果用户在登录应用程序后的那一分钟内启动操作,通常会发生这种情况(不,我不知道为什么!)......

The web-service request contains an attachment. Web服务请求包含附件。 This attachment is a PDF file that is not bigger than 15Kb (at least during all my tests, successfull or failed, this size was never exceeded). 此附件是一个不大于15Kb的PDF文件(至少在我的所有测试期间,成功或失败,从未超过此大小)。

So after many tests, I tried to call the same web-service without attaching any PDF file, and the call was successfull. 因此,经过多次测试后,我尝试在附加任何PDF文件的情况下调用相同的Web服务,并且调用成功。

For your information, if the security constraints are disabled on the ws-provider (ie we do not use 2-ways SSL connexion anymore) then the web-service calls never fail. 为了您的信息,如果在ws-provider上禁用了安全性约束(即我们不再使用双向SSL连接),那么Web服务调用永远不会失败。

So I imagine that I have to configure something on my web-services or on Tomcat (or ?) to make the system working. 所以我想我必须在我的web服务或Tomcat(或?)上配置一些东西才能使系统正常工作。 Any idea? 任何想法?


Java code Java代码

Here is the Java interface that provides the method call on ws-client (as you can see, the core Java classes used for the WS are generated by Jax-WS library): 这是在ws-client上提供方法调用的Java接口(如您所见,用于WS的核心Java类由Jax-WS库生成):

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.1.3-b02-
 * Generated source version: 2.1
 * 
 */
@WebService(name = "PushServicePortType", targetNamespace = "http://my.app.ws/PushService")
@XmlSeeAlso({
    ObjectFactory.class
})
public interface PushServicePortType {


    @WebMethod
    @WebResult(name = "response", targetNamespace = "")
    @RequestWrapper(localName = "createOrRenewRequest", targetNamespace = "http://my.app.ws/CwfPushService", className = "my.app.CreateOrRenewRequest")
    @ResponseWrapper(localName = "createOrRenewRequestResponse", targetNamespace = "http://my.app.ws/CwfPushService", className = "my.app.CreateOrRenewRequestResponse")
    public String createOrRenewRequest(
        @WebParam(name = "xmlMessageContent", targetNamespace = "")
        String xmlMessageContent,
        @WebParam(name = "version", targetNamespace = "")
        String version,
        @WebParam(name = "attachments", targetNamespace = "")
        List<DataHandler> attachments);

}

The call on ws-client is as follow ( xmlFile contains the XML for the request, datahandler is a javax.activation.DataHandler that contains the PDF attachment): ws-client的调用如下( xmlFile包含请求的XML, datahandler是包含PDF附件的javax.activation.DataHandler ):

PushServicePortType pushServicePort = new PushService(new URL("url/to/wsdl"), new QName("http://my.app.ws/PushService", "PushService")).getPushServiceSOAP(new MTOMFeature());
PushRequest push = new PushRequest();
responseXML = push.sendXMLRequest(pushServicePort, xmlFile, datahandler);

There are lots of configuration files, logs or Java classes. 有很多配置文件,日志或Java类。 So do not hesitate to ask for more details if needed! 所以如果需要,请不要犹豫,询问更多细节!


EDIT 1 编辑1

I've tried to force the integration of the PDF file within the SOAP message, by defining the MTOM threshold to 2Mb: 我试图通过将MTOM阈值定义为2Mb来强制将SOAP文件集成到SOAP消息中:

On ws-client : ws-client

PushServicePortType pushServicePort = new PushService(new URL("url/to/wsdl"), new QName("http://my.app.ws/PushService", "PushService")).getPushServiceSOAP(new MTOMFeature(2097152));

On ws-provider , I configured the MTOM annotation with @MTOM(threshold=2097152) ws-provider ,我使用@MTOM(threshold=2097152)配置了MTOM注释@MTOM(threshold=2097152)

This correction did not solve my issue, unfortunately... 不幸的是,这次修正并没有解决我的问题......

Edit 2 编辑2

I've removed the PDF attachment from my web-service all, and now it also fails but with a different message: 我已经从我的网络服务中删除了所有的PDF附件,现在它也失败但是有一个不同的消息:

javax.xml.ws.soap.SOAPFaultException: Failed to read a response: javax.xml.bind.UnmarshalException
 - with linked exception:
[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,3834]
Message: XML document structures must start and end within the same entity.]
        at com.sun.xml.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:173)
        at com.sun.xml.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:102)
        at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:240)
        at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:210)
        at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:103)
        at $Proxy33.createOrRenewRequest(Unknown Source)
        at my.app.CWFPushRequest.sendXMLRequest(PushRequest.java:29)
        ...
Caused by: com.sun.xml.ws.encoding.soap.DeserializationException: Failed to read a response: javax.xml.bind.UnmarshalException
 - with linked exception:
[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,3834]
Message: XML document structures must start and end within the same entity.]
        at com.sun.xml.ws.server.sei.EndpointMethodHandler.invoke(EndpointMethodHandler.java:235)
        at com.sun.xml.ws.server.sei.SEIInvokerTube.processRequest(SEIInvokerTube.java:74)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:559)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:518)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:503)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:400)
        at com.sun.xml.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:229)
        at com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:430)
        at com.sun.xml.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:230)
        at com.sun.xml.ws.transport.http.servlet.ServletAdapter.handle(ServletAdapter.java:121)
        at com.sun.xml.ws.transport.http.servlet.WSServletDelegate.doGet(WSServletDelegate.java:115)
        at com.sun.xml.ws.transport.http.servlet.WSServletDelegate.doPost(WSServletDelegate.java:146)
        at com.sun.xml.ws.transport.http.servlet.WSSpringServlet.doPost(WSSpringServlet.java:52)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
        at sun.reflect.GeneratedMethodAccessor330.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.catalina.security.SecurityUtil$1.run(SecurityUtil.java:244)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAsPrivileged(Subject.java:517)
        at org.apache.catalina.security.SecurityUtil.execute(SecurityUtil.java:276)
        at org.apache.catalina.security.SecurityUtil.doAsPrivilege(SecurityUtil.java:162)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:262)
        at org.apache.catalina.core.ApplicationFilterChain.access$000(ApplicationFilterChain.java:52)
        at org.apache.catalina.core.ApplicationFilterChain$1.run(ApplicationFilterChain.java:171)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:167)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:210)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:174)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:525)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
        at org.apache.catalina.valves.FastCommonAccessLogValve.invoke(FastCommonAccessLogValve.java:482)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:151)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:870)
        at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
        at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
        at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
        at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:685)
        ... 1 more
Caused by: javax.xml.bind.UnmarshalException
        at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(UnmarshallerImpl.java:397)
        at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:335)
        at com.sun.xml.bind.v2.runtime.BridgeImpl.unmarshal(BridgeImpl.java:84)
        at com.sun.xml.bind.api.Bridge.unmarshal(Bridge.java:197)
        at com.sun.xml.ws.server.sei.EndpointArgumentsBuilder$DocLit.readRequest(EndpointArgumentsBuilder.java:492)
        at com.sun.xml.ws.server.sei.EndpointMethodHandler.invoke(EndpointMethodHandler.java:233)
        ... 41 more
Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,3834]
Message: XML document structures must start and end within the same entity.
        at com.sun.xml.stream.XMLReaderImpl.next(XMLReaderImpl.java:563)
        at com.sun.xml.ws.encoding.MtomCodec$MtomXMLStreamReaderEx.next(MtomCodec.java:413)
        at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:188)
        at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:333)
        ... 45 more

I've tried to log the SOAP messages sent by ws-client , but I don't see anything wrong in it... 我试图记录ws-client发送的SOAP消息,但我没有看到任何错误...

I may have found a solution to my problem. 我可能找到了解决问题的方法。

As explained, the ws-provider Tomcat is giving me the following error: 如上所述, ws-provider Tomcat给出了以下错误:

java.net.SocketException: SSL Cert handshake timeout
        at org.apache.tomcat.util.net.jsse.JSSE14Support.synchronousHandshake(JSSE14Support.java:101)
        at org.apache.tomcat.util.net.jsse.JSSE14Support.handShake(JSSE14Support.java:67)
        at org.apache.tomcat.util.net.jsse.JSSESupport.getPeerCertificateChain(JSSESupport.java:121)

So I had a look on the synchronousHandshake method (Tomcat 5.5.23): 所以我看了一下synchronousHandshake方法(Tomcat 5.5.23):

private void synchronousHandshake(SSLSocket socket) throws IOException {
    InputStream in = socket.getInputStream();
    int oldTimeout = socket.getSoTimeout();
    socket.setSoTimeout(1000);
    byte[] b = new byte[0];
    listener.reset();
    socket.startHandshake();
    int maxTries = 60; // 60 * 1000 = example 1 minute time out
    for (int i = 0; i < maxTries; i++) {
        if (logger.isTraceEnabled())
            logger.trace("Reading for try #" + i);
        try {
            int x = in.read(b);
        } catch (SSLException sslex) {
            logger.info("SSL Error getting client Certs", sslex);
            throw sslex;
        } catch (IOException e) {
            // ignore - presumably the timeout
        }
        if (listener.completed) {
            break;
        }
    }
    socket.setSoTimeout(oldTimeout);
    if (listener.completed == false) {
        throw new SocketException("SSL Cert handshake timeout");
    }
}

As you can see, the timeout idea was not a real timeout problem, at least on my case. 正如您所看到的, 超时想法不是真正的超时问题,至少在我的情况下。 The error seems to happen because we read data from SSLSocket , and at the end, we do not consider that the handshake was completed correctly. 错误似乎发生是因为我们从SSLSocket读取数据,最后,我们不认为握手已正确完成。

Another thing is that I got another problem when removing the PDF attachment in my WS call. 另一件事是我在WS调用中删除PDF附件时遇到了另一个问题。 This error said that the XML parsing failed. 此错误表示XML解析失败。 So I suspected that the data received on the ws-provider side was chunked, for some reasons. 所以我怀疑由于某些原因,在ws-provider端收到的数据被分块了。

Then, I finally found an interesting parameter in the Tomcat Connector, which is maxSavePostSize : 然后,我终于在Tomcat连接器中找到了一个有趣的参数,即maxSavePostSize

The maximum size in bytes of the POST which will be saved/buffered by the container during FORM or CLIENT-CERT authentication. 在FORM或CLIENT-CERT身份验证期间,容器将保存/缓冲的POST的最大字节数(以字节为单位)。 For both types of authentication, the POST will be saved/buffered before the user is authenticated. 对于这两种类型的身份验证,POST将在用户通过身份验证之前保存/缓冲。 For CLIENT-CERT authentication, the POST is buffered for the duration of the SSL handshake and the buffer emptied when the request is processed. 对于CLIENT-CERT身份验证,POST会在SSL握手期间进行缓冲,并在处理请求时清空缓冲区。 For FORM authentication the POST is saved whilst the user is re-directed to the login form and is retained until the user successfully authenticates or the session associated with the authentication request expires. 对于FORM身份验证,保存POST,同时将用户重定向到登录表单并保留,直到用户成功进行身份验证或与身份验证请求关联的会话到期为止。 The limit can be disabled by setting this attribute to -1. 可以通过将此属性设置为-1来禁用该限制。 Setting the attribute to zero will disable the saving of POST data during authentication. 将该属性设置为零将禁用在身份验证期间保存POST数据。 If not specified, this attribute is set to 4096 (4 kilobytes). 如果未指定,则此属性设置为4096(4千字节)。

So I decided to modify the Tomcat Connector configuration to set the maxSavePostSize="-1" (ie the buffer is not limited to 4Kb anymore). 所以我决定修改Tomcat Connector配置来设置maxSavePostSize="-1" (即缓冲区不再限于4Kb)。

I am not 100% sure that this is the correct fix to apply in my situation, but all the tests I made today (with PDF attachment) were successfull, even the tests that always failed before... 我并非100%确定这是适用于我的情况的正确修复,但我今天所做的所有测试(使用PDF附件)都是成功的,即使是之前总是失败的测试......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM