范围bean如何在Hazelcast中触发会话创建

时间:2016-12-09 10:24:45

标签: java spring session hazelcast oauth2

我使用spring-web(4.1.7)和spring-security-oauth2(2.0.12)以及hazelcast(3.3)。

在测试此用户(没有会话)时,访问该站点并单击该链接以启动OpenId Connect登录。

我添加了一个HttpSessionListener来检查会话的创建时间。

OAuth2RestTemplate用于执行身份验证并包含以下行: OAuth2AccessToken accessToken = context.getAccessToken();

上下文对象是具有会话范围的OAuth2ClientContext bean,似乎使用此对象(不实例化它)会触发会话的创建(并且上下文存储在会话中)。

到目前为止一直很好,但我遇到的问题是我使用Hazelcast进行会话复制,并且没有创建Hazelcast会话。这是一个问题,因为当请求完成并且没有找到HttpSession的Hazelcast会话时,Hazelcast过滤器将破坏HttpSession。

我的问题是,如何触发创建Hazelcast过滤器? 用于创建会话的堆栈跟踪显示我们不会调用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 
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(真); 这将创建两者

  1. HttpSession
  2. Hazelcast Session
  3. 堆栈跟踪显示我们在创建HttpSession之前通过Hazelcast方法SpringAwareWebFilter.createNewSession。

    所以我的问题似乎是:

    使用RequestWrapper(HttpServletRequestWrapper)时.getSession() 获得/创建一个会话,这是Hazelcast意识到的。

    但是,当spring尝试创建会话时(使用会话范围bean时触发),它不会识别Hazelcast。

    这是预期的行为,即spring不知道创建Hazelcast会话,我必须找到一些解决方法吗?或者任何人都可以推荐任何进一步调试方法吗?

    在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 
    

    更新

    我发现在我的会话范围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请求对象。

1 个答案:

答案 0 :(得分:3)

Hazelcast WebFilter(以及SpringAwareFilter)将请求包装在RequestWrapper中。调用request.getSession()时,包装器会创建一个新的HazelcastSession,或者返回现有的WebFilter

这就是为什么ServletRequestListener需要在其他过滤器之前定义的原因。来自hazelcast-wm's README

  

确保将Hazelcast过滤器放在所有其他过滤器之前(如果有的话);你可以把它放在最顶层。

Servlet容器在过滤器之前处理ServletRequestListener个实例(请参阅this answer on SO)。因此,如果request.getSession()(或任何其他代码段)在WebFilter有机会包装请求之前调用HazelcastSession,则不会创建WebFilter并且会话复制赢了“工作。

确保Hazelcast request.getSession()在对function routerConfig($stateProvider, $urlRouterProvider) { $stateProvider .state('404', { url: '/404', templateUrl: '404.html' }) .state('home', { url: '/', templateUrl: 'home.html' }); $urlRouterProvider.otherwise('/404'); } 的任何调用之前解决请求。