首页 > 解决方案 > 在 Servlet 过滤器中调用容器的会话对象而不是 GemFire 的会话对象

问题描述

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

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

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

关于我们如何配置 GemFire:

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

AnnotationConfigWebApplicationContext rootContext = 
    new AnnotationConfigWebApplicationContext();

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

由于springSessionRepositoryFilterbean没有添加到过滤器链中,我们必须DelegatingFilterProxy使用以下内容显式注册过滤器:

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

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

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

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

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

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

任何帮助深表感谢。

根据@John Blum 的评论进行了重新设计,但仍然面临同样的问题。

标签: javaspringspring-sessiongemfirespring-data-gemfire

解决方案


简而言之,为了让Spring Session,特别是Spring Session for Pivotal GemFire (SSDG) 完成其工作,在向 (Web) 注册时, SessionRepositoryFilter( Javadoc , Source ) 必须是过滤器链中的第一个Servlet 过滤器应用程序容器(例如 Apache Tomcat、Eclipse Jetty 等)。

否则,如果Spring Session SessionRepositoryFilter不是过滤器链中的第一个 Servlet 过滤器,那么Spring Session将不会(尚未)拦截 HTTP 请求并且将无法行使替换 Container 会话的机会(通过包装HttpServletRequestwith SessionRepositoryFilter.SessionRepositoryRequestWrapper,请参阅此处)与Spring SessionSession提供的使用适当的提供程序(例如,使用 SSDG 的 GemFire),由在(此处)上设置的实现确定。SessionRepositorySessionRepositoryFilter

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

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

  1. 2.0.5宝石火的版本。 2.0.5Pivotal GemFire 的最新/当前版本的 Spring Session

  2. webappinitializer

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

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

这个类负责注册SessionRepositoryFilter使用 Spring 的DelegatingFilterProxy类(这里,然后这里,和这里(注意insertBeforeOtherFilters实例变量,默认为true),并且以正确的顺序,这里,嗯,特别是这里)。

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

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

您可以看到Spring Session 是 AbstractHttpServletApplicationInitializer如何在示例中使用的,例如,here。有关该示例的更多详细信息,请参见示例Initializer的相应指南文档

我注意到您的 SpringDelegatingFilterProxy类注册(以bean 命名,命名为“springSessionRepositoryFilter”)的不同之处在于,您将作为第二个参数传递给,如下所示...SessionRepositoryFilterDelegatingFilterProxy.classservletContext.addFilter("filterName", <FilterType>);

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

但是,Spring Session(核心)本身实际上构造和初始化(使用“springSessionRepositoryFilter”bean 名称)SpringDelegatingFilterProxy类的实例,并将该“实例”传递给ServletContext.addFilter(..)注册时的方法(第二个参数)。

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

深思熟虑。

希望这可以帮助!


推荐阅读