我将 spring-web (4.1.7) 与 spring-security-oauth2 (2.0.12) 和 hazelcast (3.3) 一起使用。
在测试时,用户(没有 session )访问该站点并单击链接以启动 OpenId Connect 登录。
我添加了一个 HttpSessionListener 来检查 session 何时创建。
OAuth2RestTemplate 用于执行身份验证并包含以下行: OAuth2AccessToken accessToken = context.getAccessToken();
上下文对象是一个具有 session 范围的 OAuth2ClientContext bean,似乎使用此对象(而不是实例化它)会触发 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(true); 这将创建两者
- HttpSession
- Hazelcast session
堆栈跟踪显示我们在创建 HttpSession 之前执行了 Hazelcast 方法 SpringAwareWebFilter.createNewSession。
所以我遇到的问题似乎是:
使用 RequestWrapper(HttpServletRequestWrapper).getSession() 时 获取/创建 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) 的调用。
该对象有一个名为 request 的属性,在该属性上调用 getSession(boolean)。 如果这个请求对象是 Hazelcast 过滤器创建的 WebFilter$RequestWrapper,我怀疑一切都会正常工作。
但是,在调用 Hazelcast 过滤器 doFilter 之前,ServletRequestAttributes 已使用 HttpServletRequest(未由 Hazelcast 包装)进行初始化。
似乎不可能更新 ServletRequestAttributes 中的请求属性,但也许有某种方法可以创建新的属性。
更新2
我使用的是 RequestContextListener,它在任何过滤器之前被触发,并在 Hazelcast 过滤器有机会包装请求对象之前在 ServletRequestAttributes 中设置请求对象。我删除了 RequestContextListener 并将其替换为 RequestContextFilter (似乎是他们在 spring boot 中所做的: https://github.com/spring-projects/spring-boot/issues/2637 )。这确保了当初始化 ServletRequestAttributes 时,它会获取 Hazelcast 请求对象。
最佳答案
Hazelcast WebFilter
(以及 SpringAwareFilter
)将请求包装在 RequestWrapper
中。当调用 request.getSession()
时,包装器会创建一个新的 HazelcastSession
,或者返回现有的(如果有)。
这就是为什么 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
将被创建, session 复制将不起作用。
确保 Hazelcast WebFilter
在调用 request.getSession()
解决问题之前包装请求。
关于java - 作用域 bean 应如何在 Hazelcast 中触发 session 创建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41058183/