Spring 使用 Tiles 时会话在 Tomcat 8 上不工作 - 会话 Cookie 未设置,因为已包含响应

Spring Session not working on Tomcat 8 when using Tiles - SESSION Cookie is not set as response is already included

我在 Spring 引导项目上使用 Spring Session 1.2。0.RELEASE。这被打包为 war 并部署在 Tomcat 8.

我已遵循 Spring 会话文档并正确配置了它。问题是应用程序的入口点是一个控制器,它在会话上设置了一些值,但 SESSION cookie 没有发送到浏览器。

正在调试我看到了:

  1. org.springframework.session.web.http.CookieHttpSessionStrategy.onNewSession() 尝试写入 cookie:

    this.cookieSerializer .writeCookieValue(new CookieValue(request, response, cookieValue));

  2. org.springframework.session.web.http.DefaultCookieSerializer.writeCookieValue() 在响应中设置 cookie:

    response.addCookie(sessionCookie);

  3. 实际上并没有写入cookie。底层响应对象是 org.apache.catalina.core.ApplicationHttpResponse。它的addCookie()方法是:

    /**
      *  Disallow <code>addCookie()</code> calls on an included response.         
      *  @param cookie The new cookie
      */
    @Override
    public void addCookie(Cookie cookie) {
    
        if (!included)
            ((HttpServletResponse) getResponse()).addCookie(cookie);
    
    }
    

问题是 included 属性在某些时候设置为 true,阻止了添加 cookie。

当 jsp(使用磁贴)正在维修时会发生这种情况:

更新:

这是响应被标记为包含的时刻(当 standard.jsp tiles 布局插入属性时:

<tiles:insertAttribute name="header" ignore="false"/>

为了解决这个问题,我最终创建了一个过滤器来强制创建会话。

正如所见,对控制器的第一次调用没有添加 cookie,因为在 Tiles-JSP 呈现期间,响应已被标记为包含。我所做的是强制在过滤器中创建会话并重定向询问完全相同的 requestURI。这样,由于调用不涉及呈现 cookie 的图块,因此可以在下一次调用中立即使用。

@Bean
@ConditionalOnExpression("${sessionEnforcerFilter.enabled:true}")
public FilterRegistrationBean sessionEnforcerFilter(){
    logger.info("Registering sessionEnforcerFilter");
    FilterRegistrationBean frb = new FilterRegistrationBean();
    frb.setName("sessionEnforcerFilter");
    frb.setFilter(new SessionEnforcerFilter());
    frb.setUrlPatterns(Arrays.asList(new String[]{"/*"}));
    return frb;
}

public class SessionEnforcerFilter implements Filter{

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest)request;
        HttpServletResponse httpServletResponse = (HttpServletResponse)response;
        if(httpServletRequest.getSession(false)==null){
            logger.debug("sessionEnforcerFilter.doFilter () - Session is null - forcing its creation");
            httpServletRequest.getSession();
            String requestURI = httpServletRequest.getRequestURI();
            logger.debug("sessionEnforcerFilter.doFilter () - Repeating request [{}]", requestURI);
            httpServletResponse.sendRedirect(requestURI);
        }else{
            chain.doFilter(httpServletRequest, response);
        }
    }

    @Override
    public void destroy() {}

}

总结

  1. 在SessionRepositoryResponseWrapper.onResponseCommitted()中保持断点。

  2. 检查 SessionRepositoryRequestWrapper 中的响应对象是否为非包装响应。 (包含 = 假)

  3. 如果是包装响应对象,确保sessionRepositoryFilter在前。

================

Spring-session在'DispatcherType.INCLUDE (included = true)'.

时已经在处理问题

SessionRepositoryResponseWrapper.onResponseCommitted() 正在尝试向原始响应对象添加 Cookie。

sessionRepositoryFilter 必须位于第一个位置以包装由 tomcat 传递的原始 applicationHttpResponse。

问题情况

  • SessionRepositoryRequestWrapper 接收包装的响应并保存它。

  • 在servlet容器中执行doInclude()时,找到原来的reponse,用ApplicationHttpResponse(included = true)包裹起来。 然后,SetResponse(新包装响应)到最里面的包装器。

http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat.embed/tomcat-embed-core/8.0.24/org/apache/catalina/core/ApplicationDispatcher.java#ApplicationDispatcher.doInclude%28javax.servlet.ServletRequest%2Cjavax.servlet.ServletResponse%29

http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat.embed/tomcat-embed-core/8.0.24/org/apache/catalina/core/ApplicationDispatcher.java#ApplicationDispatcher.wrapResponse%28org.apache.catalina.core.ApplicationDispatcher.State%29

  • Spring-session 对存储在 SessionRepositoryResponseWrapper.onResponseCommitted() 中的响应(期望原始响应)执行 addCookie,但它不能,因为它设置为 'included = true'.