java - 作用域 bean 应如何在 Hazelcast 中触发 session 创建

标签 java spring session hazelcast oauth-2.0

我将 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); 这将创建两者

  1. HttpSession
  2. 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/

相关文章:

java - 如何调用 AWS Lambda 函数的 ssl 端点

java - 数组搜索方法困惑

java - 仅创建接口(interface)实例的目的是什么?

java - 如何使用struts 2标签检查jsp中的list.contains

java - 什么是 Vaadin UI 以及如何在 UI 之间导航

PHP: session 检查用户

javascript - 将 jQuery 'rand color' 保存到本地存储?

java - Spring中ApplicationContext.xml和属性文件的位置

java - Spring MVC : Receive XML data and Send String message back

html - 该站点如何知道我正在打开另一个选项卡?