我已经使用Spring Security两周了,除了匿名用户和会话超时外,它还运行良好。
使用案例#1
使用案例#2
注册用户登录后可以查看受保护的页面。
如果会话超时,将显示invalidSessionUrl页面,并指示用户登录。
我已经阅读了大量的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可能会导致服务器上的过期会话出现问题(不确定这是否很重要)。 #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'
答案 0 :(得分:0)
我在这个问题的几天后发布了一个类似的问题:Why does anonymous user get redirected to expiredsessionurl by Spring Security,我已经发布了答案。上面的解决方案#2有效,但我不喜欢关闭公共页面的所有安全性。我最终实现的是#3和#4的组合。请参阅相关问题以获得完整答案。