在 Servlet Filter 中调用容器的会话对象而不是 GemFire 的会话对象

Container's session object and not GemFire's session object is invoked inside Servlet Filter

在尝试访问 GemFire 会话对象时,从自定义 Servlet 过滤器获取容器的会话对象。会话对象的类型为:

org.apache.catalina.session.StandardSessionFacade@517957e2

但从 Controller 开始,它工作正常。会话对象属于以下类型: org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@5afe18ce

关于我们如何配置 GemFire:

我们有一个遗留的零售应用程序。最重要的是,我们使用了 2.0.5 版本的 GemFire。在 webappintializer 启动时,

AnnotationConfigWebApplicationContext rootContext = 
    new AnnotationConfigWebApplicationContext();

rootContext.register(GemfireConfig.class,RootConfig.class, SecurityConfig.class);

由于 springSessionRepositoryFilter bean 添加到过滤器链中,我们必须使用以下方法向 DelegatingFilterProxy 显式注册过滤器:

FilterRegistration.Dynamic springSessionRepositoryFilter = 
    container.addFilter("springSessionRepositoryFilter", DelegatingFilterProxy.class);

springSessionRepositoryFilter.addMappingForUrlPatterns(
    EnumSet.allOf(DispatcherType.class), false, "/*");

在数据处理方面,为了获取会话对象,我们有一个 getSession 方法,其中 returns 一个会话对象:

ServletRequestAttributes attr = 
    (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

HttpSession session = attr.getRequest().getSession();

当我们从 Controller 调用 getSession() 方法时,它按设计工作得非常好。但是从 Servlet 过滤器调用相同的方法最终会得到容器创建的会话对象。

非常感谢任何帮助。

根据@John Blum 的评论进行了修改,但仍然面临同样的问题。

简而言之,为了 Spring Session, and specifically, Spring Session for Pivotal GemFire (SSDG), to do its job, the SessionRepositoryFilter (Javadoc, Source) 必须是第一个 Servlet Filter 在过滤器链中注册到您的(Web)应用程序容器(例如 Apache Tomcat、Eclipse 码头等)。

否则,如果SpringSession的SessionRepositoryFilter不是过滤器链中的第一个Servlet Filter,那么Spring Session 不会(还)拦截 HTTP 请求,也无法利用它的机会替换容器会话(通过将 HttpServletRequest 包装成 SessionRepositoryFilter.SessionRepositoryRequestWrapper,参见 here) with a Session provided by Spring Session using the appropriate provider (e.g. like GemFire with SSDG), as determined by the SessionRepository implementation, which is set on the SessionRepositoryFilter (here).

您的 Spring Web MVC 应用程序 Controller 工作的原因是,Java EE Servlet Spec/Container 保证在调用任何 Servlet 之前调用所有 Servlet 过滤器使用 HTTP 请求(即 HttpServletRequest)。而且,由于 Spring Web MVC DispatcherServlet 是一个合适的 HttpServlet 并且负责调用您的应用程序定义的 Spring Web MVC Controllers,那么应用程序 Controllers 保证看到 "replaced" HTTP 请求(以及扩展的 HTTP 会话对象)。

所以,一些家务用品......我不是(完全)确定你的意思:

  1. 2.0.5 版本的 GemFire。 2.0.5 指的是 Pivotal GemFire.

    Spring 版本的 latest/current 版本
  2. webappinitializer?

对于 #2,您的意思是您专门创建了一个 Spring WebApplicationInitializer 来手动显式注册 SessionRepositoryFilter(如上面的代码片段所示)?

你知道吗Spring Session已经提供了这样一个class...o.s.session.web.context.AbstractHttpServletApplicationInitializer.

这个class负责使用Spring的DelegatingFilterProxyclass(here, then here, and here (notice the insertBeforeOtherFilters instance variable, which defaults to true), and in the right order, here, well, specifically, here)注册SessionRepositoryFilter

有趣的是,您似乎在上面的过滤器注册代码片段中做了相同或相似的事情。

注意:此(Servlet 容器的编程配置)仅适用于 Servlet 3.0 容器及更高版本。

您可以看到 Spring 会话的 AbstractHttpServletApplicationInitializer 如何在 samples, for instance, here. More details on the Initializer are in the corresponding guide docs 示例中使用。

我注意到你的 Spring DelegatingFilterProxy class 注册(以 SessionRepositoryFilter bean 命名,命名为 "springSessionRepositoryFilter")有一点不同,你将 DelegatingFilterProxy.class 作为第二个参数传递给 servletContext.addFilter("filterName", <FilterType>);,如下所示...

FilterRegistration.Dynamic springSessionRepositoryFilter = 
    container.addFilter("springSessionRepositoryFilter", DelegatingFilterProxy.class);

然而,SpringSession(核心)本身实际上constructs and initializes (with the "springSessionRepositoryFilter" bean name) an instance of the Spring DelegatingFilterProxy class and passes那个"instance"到ServletContext.addFilter(..)方法(第二个参数) 注册时。

我怀疑 ServletContext.addFilter(..) API 本身只是在 constructing/initializing 实例时使用 Spring DelegatingProxyFilter class 的默认构造函数,这可能是您问题的根源,尤其是在以编程方式注册 Servlet 过滤器时。

深思。

希望对您有所帮助!

尝试了这些建议后问题仍然存在。现在,我们不再使用单独注册的SessionRepositoryFilter。我们现在使用:

class WebAppInitializer extends AbstractHttpSessionApplicationInitializer {

 public WebAppInitializer() {
        super(GemfireConfig.class,X.class, Y.class); //X&Y are preexisting config classes in the application context

    }

@Override
public void onStartup(ServletContext container) throws ServletException {

    super.onStartup(container); // newly added inorder to call AbstractHttpSessionApplicationInitializer.startup()
    ...//existing code
    ...//

}

去掉了springSessionRepositoryFilter的显式注册。

当我们查看 Stack Trace 时,我们可以看到这个过滤器首先被调用。所以,过滤器链的顺序似乎是完整的。

我们遇到问题的过滤器随后被调用。即使没有提到的更改,这也是相同的行为。

仍然,过滤器中的会话对象来自容器。