【问题标题】:Container's session object and not GemFire's session object is invoked inside Servlet Filter在 Servlet 过滤器中调用容器的会话对象而不是 GemFire 的会话对象
【发布时间】:2018-09-12 14:11:16
【问题描述】:

当尝试访问 GemFire 会话对象时,从自定义 Servlet 过滤器中,它会取而代之的是 Container 的会话对象。会话对象的类型:

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 方法,它返回一个会话对象:

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

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

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

非常感谢任何帮助。

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

【问题讨论】:

    标签: java spring spring-session gemfire spring-data-gemfire


    【解决方案1】:

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

    否则,如果 Spring Session 的 SessionRepositoryFilter 不是过滤器链中的第一个 Servlet 过滤器,那么 Spring Session 将不会(尚未)拦截到 HTTP 请求并且将无法使用 Spring Session 提供的 Session 替换 Container 会话(通过将 HttpServletRequest 包装为 SessionRepositoryFilter.SessionRepositoryRequestWrapper,参见 here)使用适当的提供者(例如带有 SSDG 的 GemFire),由 SessionRepository 实现确定,该实现在 SessionRepositoryFilter (here) 上设置。

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

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

    1. 2.0.5 GemFire 版本。 2.0.5 指的是最新/当前版本的Pivotal GemFire 春季会议

    2. 还有webappinitializer?

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

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

    这个类负责注册SessionRepositoryFilter使用Spring的DelegatingFilterProxy类(here,然后here,和here(注意insertBeforeOtherFilters实例变量,默认为true),并且按照正确的顺序,here,嗯,具体来说,here)。

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

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

    您可以看到 Spring Session 的 AbstractHttpServletApplicationInitializer 如何在 samples 中使用,例如 here。有关Initializer 的更多详细信息,请参见示例对应的guide docs

    我注意到你的 Spring DelegatingFilterProxy 类注册(以 SessionRepositoryFilter bean 命名,命名为“springSessionRepositoryFilter”)的不同之处在于,你将 DelegatingFilterProxy.class 作为第二个参数传递给 servletContext.addFilter("filterName", <FilterType>);,如图所示……

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

    然而,Spring Session(核心)本身实际上是 constructs and initializes(带有“springSessionRepositoryFilter”bean 名称)Spring DelegatingFilterProxy 类的一个实例,而passes 是该“实例”到ServletContext.addFilter(..) 注册方法(第二个参数)。

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

    值得深思。

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      尝试了建议后问题仍然存在。现在,我们不再对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 的显式注册。

      当我们查看堆栈跟踪时,我们可以看到首先调用了这个过滤器。因此,过滤器链的顺序似乎是完整的。

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

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

      【讨论】:

      • 您不需要重写onStartup(:ServletContext) 方法(次要细节,:-) 没关系,但是您是否使用web.xml 注册自定义的、特定于应用程序的Servlet 过滤器?因此,如果您从 Stack Trace 中看到 SessionRepositoryFilter 是 Servlet 过滤器链中的第一个,那么在查看原始帖子中的代码后,我有点想知道这个 RequestContextHolder.currentRequestAttributes(); 之后在做什么。 ..HttpSession session = attr.getRequest().getSession();?
      • 看来您正在使用核心 Spring Framework 中的 RequestContextHolderServletRequestAttributes 类来获取 HttpServletRequest 后跟 HttpSession。我怀疑在springSessionRepositoryFilter 有机会“替换”HTTP 请求/会话对象之后,Spring Session 核心可能无法正确更新 Spring 的RequestContextHolder。打算做更多的挖掘......
      • 所以,在进一步挖掘之后...... 1)更新RequestContextHolder状态不是Spring Session(特别是SessionRepositoryFilter)的责任。 2) 您为什么在自定义的、特定于应用程序的 Servlet 过滤器中使用 RequestContextHolder 类(正如我们之前讨论的,需要在过滤器链中的 SessionRepositoryFilter 之后运行)来访问 HttpServletRequest 和随后访问/启动HttpSession,因为Filter 接口,doFilter(..) 方法接受ServletRequestServletResponse 实例。
      • 见这里 (javaee.github.io/javaee-spec/javadocs/javax/servlet/…)。您可以简单地将 ServletRequest 转换为 HttpServletRequest 并在自定义的、特定于应用程序的 Servlet 过滤器的 doFilter(:ServletRequest, :ServletResponse) 方法中调用 ((HttpServletRequest) servletRequest).getSession()
      • 但是,如果您必须使用 Spring 的 RequestContextHolder 类访问 HTTP 请求和会话,无论出于何种原因(这对我来说并不明显,为什么)在您的自定义应用程序特定 Servlet Filter ,那么您还可以使用核心 Spring 的 RequestContextFilter (docs.spring.io/spring/docs/current/javadoc-api/org/…),如 RequestContextHolder (docs.spring.io/spring/docs/current/javadoc-api/org/…) 的 Javadoc 中所述。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      • 1970-01-01
      • 2011-03-06
      • 2012-03-09
      • 2012-09-07
      相关资源
      最近更新 更多