在 IE11 的 iframe 中提交表单在恢复 JSF 状态时抛出 java.lang.StringIndexOutOfBoundsException

Submitting form in iframe in IE11 throws java.lang.StringIndexOutOfBoundsException while restoring JSF state

用户从会话 bean 重定向后,抛出以下异常:

StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.substring(String.java:1911
 at com.sun.faces.renderkit.ServerSideStateHelper.getState(ServerSideStateHelper.java:266
 at com.sun.faces.renderkit.ResponseStateManagerImpl.getState(ResponseStateManagerImpl.java:100
 at com.sun.faces.application.view.StateManagementStrategyImpl.restoreView(StateManagementStrategyImpl.java:227
 at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:188
 at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:123
 at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:453
 at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:148
 at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:303
 at org.omnifaces.viewhandler.RestorableViewHandler.restoreView(RestorableViewHandler.java:66
 at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:303
 at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:192
 at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101
 at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116
 at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118
 at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593
 at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550
 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281
 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175
 at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655
 at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595
 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161
 at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331
 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231
 at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317
 at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195
 at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860
 at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757
 at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056
 at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229
 at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137
 at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104
 at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90
 at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79
 at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54
 at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59
 at com.sun.grizzly.ContextTask.run(ContextTask.java:71
 at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532
 at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513
 at java.lang.Thread.run(Thread.java:745)

按钮代码使用actionListener属性:

actionListener="#{aController.aMethod}"

控制器像这样执行重定向:

ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
String encodedURL = response.encodeURL(context.getRequestContextPath() + "/page.xhtml?faces-redirect=true");
context.redirect(encodedURL);

我的会话配置:

<session-config>
    <session-timeout>60</session-timeout>
    <tracking-mode>URL</tracking-mode>
</session-config>

该页面仅包含一个表单(以及生成的 HTML 输出),定义:

<form id="wrapper" name="wrapper" method="post" action="/ACETOwebkalender/a/b.xhtml" enctype="application/x-www-form-urlencoded">

外页使用Slider Revolution Responsive WordPress Plugin,外页使用jQuery.ajax。这会产生问题吗?

此问题仅出现在 IE11 和 Safari 中,当页面使用 iFrame 嵌入到另一个页面时。

在每个页面上使用以下代码片段修复了 Safari 的错误:

    <script>
    window.onload = function() {
        if (top != self) { // If true, then page is been requested inside an iframe.
            var jsessionid = '#{session.id}';
            var forms = document.forms;

            for (i = 0; i &lt; forms.length; i++) {
                forms[i].action += ';JSESSIONID=' + jsessionid;
          }
        }
    };
</script>

对于 IE,我将 web.xml 更新为以下内容:

....
<filter>
    <filter-name>P3P Response Filter</filter-name>
    <filter-class>util.ResponseFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>P3P Response Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<session-config>
    <session-timeout>60</session-timeout>
    <cookie-config>
        <http-only>true</http-only>
        <secure>true</secure>
    </cookie-config>
    <tracking-mode>COOKIE</tracking-mode>
    <tracking-mode>URL</tracking-mode>
</session-config>
...

其中 ResponseFilter.class 是:

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class ResponseFilter implements Filter {

 @Override
 public void init(FilterConfig filterConfig) throws ServletException {}

 @Override
 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  HttpServletResponse resp = (HttpServletResponse) response;
  resp.addHeader("P3P", "CP=\"DC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"");
  chain.doFilter(request, resp);
 }

 @Override
 public void destroy() {}
}