作用域 bean 应该如何触发 session 在 Hazelcast 中创建
How should a scoped bean trigger session creation in Hazelcast
我正在使用 spring-web (4.1.7) 与 spring-security-oauth2 (2.0.12) 和 hazelcast (3.3)。
在测试时,用户(没有 session)访问该站点并单击 link 以启动 OpenId Connect 登录。
我添加了一个 HttpSessionListener 来检查 session 何时创建。
OAuth2RestTemplate 用于执行身份验证并包含以下行:
OAuth2AccessToken accessToken = context.getAccessToken();
上下文 object 是一个具有 session 作用域的 OAuth2ClientContext bean,似乎使用这个 object (不实例化它)会触发创建 session (并且上下文存储在 session).
到目前为止一切顺利,但我遇到的问题是我正在使用 Hazelcast 进行 session 复制,但没有创建 Hazelcast session。这是一个问题,因为当请求完成并且没有为 HttpSession 找到 Hazelcast session 时,Hazelcast 过滤器将破坏 HttpSession.
我的问题是,应该如何触发 Hazelcast 过滤器的创建?
创建 session 的堆栈跟踪显示我们没有调用 Hazelcast 来创建 session:
WebSessionListener.sessionCreated(HttpSessionEvent) line: 15
StandardSession.tellNew() line: 367
StandardSession.setId(String) line: 341
StandardManager(ManagerBase).createSession(String) line: 857
StandardManager.createSession(String) line: 291
Request.doGetSession(boolean) line: 2606
Request.getSession(boolean) line: 2316
RequestFacade.getSession(boolean) line: 841
ServletRequestAttributes.getSession(boolean) line: 111
ServletRequestAttributes.getSessionMutex() line: 244
SessionScope.get(String, ObjectFactory<?>) line: 91
DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 337
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 194
SimpleBeanTargetSource.getTarget() line: 35
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 187
$Proxy322.getAccessToken() line: not available
OAuth2RestTemplate.getAccessToken() line: 169
我有一些技巧,发现如果我打电话给
request.getSession(真);
这将创建 both
- HttpSession
- The Hazelcast Session
堆栈跟踪显示我们在创建 HttpSession.
之前通过了 Hazelcast 方法 SpringAwareWebFilter.createNewSession
所以我遇到的问题似乎是:
使用 RequestWrapper(HttpServletRequestWrapper).getSession() 时
到 get/create a session 这是 Hazelcast 知道的。
然而,当 spring 尝试创建一个 session(当使用 session 作用域 bean 时触发),它是 Hazelcast 感知不到的。
这是预期的行为吗,即 spring 不知道创建 Hazelcast session 并且我必须找到一些解决方法?或者有人可以推荐任何进一步的调试方法吗?
在 web.xml 中,hazelcast 配置是:
<filter>
<filter-name>hazelcast-filter</filter-name>
<filter-class>com.hazelcast.web.spring.SpringAwareWebFilter</filter-class>
<init-param>
<param-name>map-name</param-name>
<param-value>sessions</param-value>
</init-param>
<init-param>
<param-name>sticky-session</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>cookie-name</param-name>
<param-value>hazelcast.session</param-value>
</init-param>
<init-param>
<param-name>cookie-secure</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>cookie-http-only</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>config-location</param-name>
<param-value>hazelcast-geneva.xml</param-value>
</init-param>
<init-param>
<param-name>shutdown-on-destroy</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hazelcast-filter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
作为参考,如果我强制调用 request.getSession 并且此调用通过 Hazelcast 过滤器 WebFilter 和 SpringAwareWebFilter,则堆栈链是这样的(与上面缺少 Hazelcast 过滤器的堆栈相比)
WebSessionListener.sessionCreated(HttpSessionEvent) line: 15
StandardSession.tellNew() line: 367
StandardSession.setId(String) line: 341
StandardManager(ManagerBase).createSession(String) line: 857
StandardManager.createSession(String) line: 291
Request.doGetSession(boolean) line: 2606
Request.getSession(boolean) line: 2316
RequestFacade.getSession(boolean) line: 841
WebFilter$RequestWrapper(HttpServletRequestWrapper).getSession(boolean) line: 255
WebFilter$RequestWrapper.getOriginalSession(boolean) line: 533
SpringAwareWebFilter(WebFilter).createNewSession(WebFilter$RequestWrapper, String) line: 307
SpringAwareWebFilter.createNewSession(WebFilter$RequestWrapper, String) line: 47
WebFilter$RequestWrapper.getSession(boolean) line: 605
WebFilter$RequestWrapper.getSession(boolean) line: 515
RequestWrapper(HttpServletRequestWrapper).getSession(boolean) line: 255
更新
我发现当在我的 session 作用域 bean 上调用一个方法时,它会触发对 ServletRequestAttributes.getSession(boolean) 的调用。
这个 object 有一个名为 request 的属性,它在该属性上调用 getSession(boolean)。
如果这个请求 object 是由 Hazelcast 过滤器创建的 WebFilter$RequestWrapper,我怀疑一切都会正常工作。
然而,在调用 Hazelcast 过滤器 doFilter 之前,ServletRequestAttributes 已使用 HttpServletRequest(未被 Hazelcast 包装)初始化。
似乎无法更新 ServletRequestAttributes 中的请求属性,但也许有一些方法可以创建一个新的。
更新 2
我正在使用 RequestContextListener,它在任何过滤器之前被触发,并在 Hazelcast 过滤器有机会包装它之前在 ServletRequestAttributes 中设置请求 object。我删除了 RequestContextListener 并将其替换为 RequestContextFilter(这似乎是他们在 spring 启动时所做的:https://github.com/spring-projects/spring-boot/issues/2637)。这确保在初始化 ServletRequestAttributes 时,它会收到 Hazelcast 请求 object.
Hazelcast WebFilter
(以及 SpringAwareFilter
)将请求包装在 RequestWrapper
中。当 request.getSession()
被调用时,包装器会创建一个新的 HazelcastSession
,或者 returns 现有的 returns 如果有的话。
这就是为什么 WebFilter
需要在其他过滤器之前定义的原因。来自 hazelcast-wm's README:
Make sure Hazelcast filter is placed before all the other filters if any; you can put it at the top.
Servlet 容器在过滤器之前处理 ServletRequestListener
个实例(参见 this answer on SO)。因此,如果 ServletRequestListener
(或任何其他代码片段)在 WebFilter
有机会包装请求之前调用 request.getSession()
,则不会创建 HazelcastSession
,会话复制也不会”没用。
确保 Hazelcast WebFilter
在对 request.getSession()
的任何调用之前包装请求可以解决问题。
我正在使用 spring-web (4.1.7) 与 spring-security-oauth2 (2.0.12) 和 hazelcast (3.3)。
在测试时,用户(没有 session)访问该站点并单击 link 以启动 OpenId Connect 登录。
我添加了一个 HttpSessionListener 来检查 session 何时创建。
OAuth2RestTemplate 用于执行身份验证并包含以下行: OAuth2AccessToken accessToken = context.getAccessToken();
上下文 object 是一个具有 session 作用域的 OAuth2ClientContext bean,似乎使用这个 object (不实例化它)会触发创建 session (并且上下文存储在 session).
到目前为止一切顺利,但我遇到的问题是我正在使用 Hazelcast 进行 session 复制,但没有创建 Hazelcast session。这是一个问题,因为当请求完成并且没有为 HttpSession 找到 Hazelcast session 时,Hazelcast 过滤器将破坏 HttpSession.
我的问题是,应该如何触发 Hazelcast 过滤器的创建? 创建 session 的堆栈跟踪显示我们没有调用 Hazelcast 来创建 session:
WebSessionListener.sessionCreated(HttpSessionEvent) line: 15
StandardSession.tellNew() line: 367
StandardSession.setId(String) line: 341
StandardManager(ManagerBase).createSession(String) line: 857
StandardManager.createSession(String) line: 291
Request.doGetSession(boolean) line: 2606
Request.getSession(boolean) line: 2316
RequestFacade.getSession(boolean) line: 841
ServletRequestAttributes.getSession(boolean) line: 111
ServletRequestAttributes.getSessionMutex() line: 244
SessionScope.get(String, ObjectFactory<?>) line: 91
DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 337
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 194
SimpleBeanTargetSource.getTarget() line: 35
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 187
$Proxy322.getAccessToken() line: not available
OAuth2RestTemplate.getAccessToken() line: 169
我有一些技巧,发现如果我打电话给 request.getSession(真); 这将创建 both
- HttpSession
- The Hazelcast Session
堆栈跟踪显示我们在创建 HttpSession.
之前通过了 Hazelcast 方法 SpringAwareWebFilter.createNewSession所以我遇到的问题似乎是:
使用 RequestWrapper(HttpServletRequestWrapper).getSession() 时 到 get/create a session 这是 Hazelcast 知道的。
然而,当 spring 尝试创建一个 session(当使用 session 作用域 bean 时触发),它是 Hazelcast 感知不到的。
这是预期的行为吗,即 spring 不知道创建 Hazelcast session 并且我必须找到一些解决方法?或者有人可以推荐任何进一步的调试方法吗?
在 web.xml 中,hazelcast 配置是:
<filter>
<filter-name>hazelcast-filter</filter-name>
<filter-class>com.hazelcast.web.spring.SpringAwareWebFilter</filter-class>
<init-param>
<param-name>map-name</param-name>
<param-value>sessions</param-value>
</init-param>
<init-param>
<param-name>sticky-session</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>cookie-name</param-name>
<param-value>hazelcast.session</param-value>
</init-param>
<init-param>
<param-name>cookie-secure</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>cookie-http-only</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>config-location</param-name>
<param-value>hazelcast-geneva.xml</param-value>
</init-param>
<init-param>
<param-name>shutdown-on-destroy</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hazelcast-filter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
作为参考,如果我强制调用 request.getSession 并且此调用通过 Hazelcast 过滤器 WebFilter 和 SpringAwareWebFilter,则堆栈链是这样的(与上面缺少 Hazelcast 过滤器的堆栈相比)
WebSessionListener.sessionCreated(HttpSessionEvent) line: 15
StandardSession.tellNew() line: 367
StandardSession.setId(String) line: 341
StandardManager(ManagerBase).createSession(String) line: 857
StandardManager.createSession(String) line: 291
Request.doGetSession(boolean) line: 2606
Request.getSession(boolean) line: 2316
RequestFacade.getSession(boolean) line: 841
WebFilter$RequestWrapper(HttpServletRequestWrapper).getSession(boolean) line: 255
WebFilter$RequestWrapper.getOriginalSession(boolean) line: 533
SpringAwareWebFilter(WebFilter).createNewSession(WebFilter$RequestWrapper, String) line: 307
SpringAwareWebFilter.createNewSession(WebFilter$RequestWrapper, String) line: 47
WebFilter$RequestWrapper.getSession(boolean) line: 605
WebFilter$RequestWrapper.getSession(boolean) line: 515
RequestWrapper(HttpServletRequestWrapper).getSession(boolean) line: 255
更新
我发现当在我的 session 作用域 bean 上调用一个方法时,它会触发对 ServletRequestAttributes.getSession(boolean) 的调用。
这个 object 有一个名为 request 的属性,它在该属性上调用 getSession(boolean)。 如果这个请求 object 是由 Hazelcast 过滤器创建的 WebFilter$RequestWrapper,我怀疑一切都会正常工作。
然而,在调用 Hazelcast 过滤器 doFilter 之前,ServletRequestAttributes 已使用 HttpServletRequest(未被 Hazelcast 包装)初始化。
似乎无法更新 ServletRequestAttributes 中的请求属性,但也许有一些方法可以创建一个新的。
更新 2
我正在使用 RequestContextListener,它在任何过滤器之前被触发,并在 Hazelcast 过滤器有机会包装它之前在 ServletRequestAttributes 中设置请求 object。我删除了 RequestContextListener 并将其替换为 RequestContextFilter(这似乎是他们在 spring 启动时所做的:https://github.com/spring-projects/spring-boot/issues/2637)。这确保在初始化 ServletRequestAttributes 时,它会收到 Hazelcast 请求 object.
Hazelcast WebFilter
(以及 SpringAwareFilter
)将请求包装在 RequestWrapper
中。当 request.getSession()
被调用时,包装器会创建一个新的 HazelcastSession
,或者 returns 现有的 returns 如果有的话。
这就是为什么 WebFilter
需要在其他过滤器之前定义的原因。来自 hazelcast-wm's README:
Make sure Hazelcast filter is placed before all the other filters if any; you can put it at the top.
Servlet 容器在过滤器之前处理 ServletRequestListener
个实例(参见 this answer on SO)。因此,如果 ServletRequestListener
(或任何其他代码片段)在 WebFilter
有机会包装请求之前调用 request.getSession()
,则不会创建 HazelcastSession
,会话复制也不会”没用。
确保 Hazelcast WebFilter
在对 request.getSession()
的任何调用之前包装请求可以解决问题。