如何使用Spring Security绕过匿名用户的会话超时?

时间:2015-09-22 15:41:08

标签: spring-security

我已经使用Spring Security两周了,除了匿名用户和会话超时外,它还运行良好。

使用案例#1

  1. 匿名用户可以访问该网站并查看公共页面(/ home,/ about,/ signup等),只要他们愿意(没有会话超时)。
  2. 如果用户选择受保护的页面,则会显示登录屏幕。
  3. 使用案例#2

    1. 注册用户登录后可以查看受保护的页面。

    2. 如果会话超时,将显示invalidSessionUrl页面,并指示用户登录。

    3. 我已经阅读了大量的SO和博客帖子,但我似乎无法找到用例#1的解决方案。我正在使用Spring 4.1.6.RELEASE,Spring Security 4.0.2 RELEASE和Tomcat8。

      这是我的安全配置:

          protected void configure(HttpSecurity http) throws Exception {
          http
          .authorizeRequests()
              .antMatchers("/", "/home", "/about", "/login**", "/thankyou", "/user/signup**", "/errors/**").permitAll() 
              .regexMatchers("/user/signup/.*").permitAll()
              .antMatchers("/recipe/listRecipes*").hasAuthority("GUEST")
              .antMatchers("/recipe/addRecipe*").hasAuthority("AUTHOR")
              .antMatchers("/admin/**").hasAuthority("ADMIN")
              .anyRequest().authenticated()
              .expressionHandler(secExpressionHandler())
              .and()
          .formLogin()
              .loginPage("/login")
              .failureUrl("/login?err=1")
              .permitAll()
              .and()
          .logout()
              .logoutSuccessUrl("/thankyou")
              .deleteCookies( "JSESSIONID" )
              .invalidateHttpSession(false)
              .and()
          .exceptionHandling()
              .accessDeniedPage("/errors/403")
              .and()
          .rememberMe()
              .key("recipeOrganizer")
              .tokenRepository(persistentTokenRepository())
              .tokenValiditySeconds(960)      //.tokenValiditySeconds(1209600)
              .rememberMeParameter("rememberMe");
      
          http
          .sessionManagement()
              .sessionAuthenticationErrorUrl("/errors/402")   
              .invalidSessionUrl("/errors/invalidSession")
              .maximumSessions(1)
              .maxSessionsPreventsLogin(true)
              .expiredUrl("/errors/expiredSession")
              .and()
              .sessionFixation().migrateSession();
      

      会话到期后匿名用户的任何操作都会导致会话异常无效。问题出在安全过滤器链中(参见下面的日志)AnonymousAuthenticationFilter(#11)创建一个新会话,但SessionManagementFilter(#12)检索先前过期的会话,将其与新的会话进行比较一个并抛出invalidsession异常。我希望这对于登录用户来说会发生,但对于匿名用户则不会。但是,在抛出异常时,安全上下文已被破坏,因此我无法知道先前的会话是来自匿名用户还是登录用户。

      我考虑的解决方案是:

      1. 将全局会话超时设置为24小时或更长时间,然后在LoginSuccessHandler和RememberMeSuccessHandler中调整注册用户的超时。
      2. 关闭公共页面的安全性。
      3. 创建一个单独的cookie来指示用户的类型(匿名与登录),并在InvalidSessionStrategy中查询以将用户重定向到/ home页面。
      4. 创建一个首先执行的自定义过滤器,以识别匿名用户(可能?)并简单地扩展当前会话。
      5. 写一些javascript或jquery来定期检查会话超时并为anon用户重置它。
      6. 解决方案#1可能会导致服务器上的过期会话出现问题(不确定这是否很重要)。 #2对我来说不合适,特别是如果公共页面可能包含有关已登录用户的任何信息。 #3可能有效,除了anon用户可以点击主页以外的公共链接但被重定向到主页,因为我认为没有办法告诉InvalidSessionStrategy他们试图访问哪个链接?我不确定#4是否会起作用 - 还没有尝试过。 #5可能也有效但会增加网络流量?

        我希望有人能指出我一个切实可行的解决方案。这必须是许多网站所处理的事情,但我正在试图解决这个问题。提前感谢任何建议或提示。

        这是日志的一部分,用于说明会发生什么。为了测试目的,我将会话超时设置为60秒。

        Initial access to website
        20:49:29.823 DEBUG: org.springframework.security.web.FilterChainProxy - / at position 11 of 14 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
        20:49:29.839 DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
        20:49:29.839 DEBUG: org.springframework.security.web.FilterChainProxy - / at position 12 of 14 in additional filter chain; firing Filter: 'SessionManagementFilter'
        20:49:29.839 DEBUG: org.springframework.security.web.FilterChainProxy - / at position 13 of 14 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
        20:49:29.839 DEBUG: org.springframework.security.web.FilterChainProxy - / at position 14 of 14 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
        20:49:29.839 DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/'
        20:49:29.839 DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /; Attributes: [permitAll]
        20:49:29.854 DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
        20:49:29.871 DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@ff577, returned: 1
        20:49:29.871 DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Authorization successful
        20:49:30.136 DEBUG: net.mycompany.myapp.config.SessionListener - AuthenticationSuccess - principal: anonymousUser
        20:49:30.167 DEBUG: net.mycompany.myapp.config.SessionListener - AuthenticationSuccess - authority contains: ROLE_ANONYMOUS
        20:49:30.167 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session created on: Mon Sep 21 20:49:30 CDT 2015
        20:49:30.167 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session last accessed on: Mon Sep 21 20:49:30 CDT 2015
        20:49:30.167 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session expires after: 60 seconds
        20:49:30.167 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session ID: CA34FE74B56B8EF94181B1231A7D4FF6
        
        Session times out after 60 seconds
        20:50:46.015 INFO : net.mycompany.myapp.config.SessionDestroyedListener - destroyedEvent
        20:50:46.015 INFO : net.mycompany.myapp.config.SessionDestroyedListener - object:org.springframework.security.web.session.HttpSessionDestroyedEvent[source=org.apache.catalina.session.StandardSessionFacade@17827f3e]
        20:50:46.015 INFO : net.mycompany.myapp.config.SessionListener - sessionDestroyed: Session created on: Mon Sep 21 20:49:30 CDT 2015
        20:50:46.015 INFO : net.mycompany.myapp.config.SessionListener - sessionDestroyed: Session last accessed on: Mon Sep 21 20:49:32 CDT 2015
        20:50:46.015 INFO : net.mycompany.myapp.config.SessionListener - sessionDestroyed: Session expires after: 60 seconds
        20:50:46.015 INFO : net.mycompany.myapp.config.SessionListener - sessionDestroyed: Session ID: CA34FE74B56B8EF94181B1231A7D4FF6
        
        Click on "/home" link (unsecured)
        20:50:53.927 DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/home'; against '/resources/**'
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 1 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 2 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No HttpSession currently exists
        20:50:53.927 DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 3 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@4c668dec
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 4 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 5 of 14 in additional filter chain; firing Filter: 'LogoutFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /home' doesn't match 'POST /logout
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 6 of 14 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /home' doesn't match 'POST /login
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 7 of 14 in additional filter chain; firing Filter: 'ConcurrentSessionFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 8 of 14 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 9 of 14 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 10 of 14 in additional filter chain; firing Filter: 'RememberMeAuthenticationFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 11 of 14 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
        20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 12 of 14 in additional filter chain; firing Filter: 'SessionManagementFilter'
        20:50:53.927 DEBUG: org.springframework.security.web.session.SessionManagementFilter - Requested session ID CA34FE74B56B8EF94181B1231A7D4FF6 is invalid.
        20:50:53.927 DEBUG: org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy - Starting new session (if required) and redirecting to '/errors/invalidSession'
        20:50:53.927 DEBUG: net.mycompany.myapp.config.SessionListener - AuthenticationSuccess - principal: anonymousUser
        20:50:53.927 DEBUG: net.mycompany.myapp.config.SessionListener - AuthenticationSuccess - authority contains: ROLE_ANONYMOUS
        20:50:53.927 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session created on: Mon Sep 21 20:50:53 CDT 2015
        20:50:53.927 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session last accessed on: Mon Sep 21 20:50:53 CDT 2015
        20:50:53.942 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session expires after: 60 seconds
        20:50:53.942 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session ID: 6F7FD6230BC075B7768648BBBC08E3F4
        20:50:53.942 DEBUG: org.springframework.security.web.session.HttpSessionEventPublisher - Publishing event: org.springframework.security.web.session.HttpSessionCreatedEvent[source=org.apache.catalina.session.StandardSessionFacade@412cbe09]
        20:50:53.942 DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to '/myapp/errors/invalidSession'
        

1 个答案:

答案 0 :(得分:0)

我在这个问题的几天后发布了一个类似的问题:Why does anonymous user get redirected to expiredsessionurl by Spring Security,我已经发布了答案。上面的解决方案#2有效,但我不喜欢关闭公共页面的所有安全性。我最终实现的是#3和#4的组合。请参阅相关问题以获得完整答案。