Spring-session: Spring Session does not wrap HttpSession on Error Dispatch on Jetty

Created on 28 Dec 2018  路  14Comments  路  Source: spring-projects/spring-session

Summary

Spring Security throws the exception when Spring-session is in use and CSRF token has not been provided.

Actual Behavior

When Spring security and Spring-session is used in application, when CSRF token is not provided, Spring security throws following exception:

java.lang.IllegalStateException: Cannot invoke saveContext on response org.springframework.security.web.firewall.FirewalledResponse@10d467dc. You must use the HttpRequestResponseHolder.response after invoking loadContext
    at org.springframework.security.web.context.HttpSessionSecurityContextRepository.saveContext(HttpSessionSecurityContextRepository.java:144) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:115) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:157) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) ~[spring-session-core-2.1.2.RELEASE.jar:2.1.2.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:566) ~[jetty-security-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1340) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1242) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:203) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.Dispatcher.error(Dispatcher.java:81) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ErrorHandler.doError(ErrorHandler.java:119) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ErrorHandler.handle(ErrorHandler.java:78) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.springframework.boot.web.embedded.jetty.JettyEmbeddedErrorHandler.handle(JettyEmbeddedErrorHandler.java:55) ~[spring-boot-2.1.1.RELEASE.jar:2.1.1.RELEASE]
    at org.eclipse.jetty.server.Response.sendError(Response.java:655) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:123) ~[javax.servlet-api-4.0.1.jar:4.0.1]
    at org.springframework.session.web.http.OnCommittedResponseWrapper.sendError(OnCommittedResponseWrapper.java:117) ~[spring-session-core-2.1.2.RELEASE.jar:2.1.2.RELEASE]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:123) ~[javax.servlet-api-4.0.1.jar:4.0.1]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:123) ~[javax.servlet-api-4.0.1.jar:4.0.1]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.sendError(OnCommittedResponseWrapper.java:119) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:123) ~[javax.servlet-api-4.0.1.jar:4.0.1]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.sendError(OnCommittedResponseWrapper.java:119) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.access.AccessDeniedHandlerImpl.handle(AccessDeniedHandlerImpl.java:76) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:118) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:157) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:151) ~[spring-session-core-2.1.2.RELEASE.jar:2.1.2.RELEASE]
    at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:81) ~[spring-session-core-2.1.2.RELEASE.jar:2.1.2.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548) ~[jetty-security-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1340) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473) ~[jetty-servlet-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1242) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.Server.handle(Server.java:503) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:364) ~[jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260) [jetty-server-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305) [jetty-io-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) [jetty-io-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:411) [jetty-io-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:305) [jetty-io-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:159) [jetty-io-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) [jetty-io-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118) [jetty-io-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333) [jetty-util-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310) [jetty-util-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168) [jetty-util-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126) [jetty-util-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366) [jetty-util-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765) [jetty-util-9.4.12.v20180830.jar:9.4.12.v20180830]
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683) [jetty-util-9.4.12.v20180830.jar:9.4.12.v20180830]
    at java.base/java.lang.Thread.run(Thread.java:844) [na:na]

Thist stacktrace is also printed on web page instead of standard Whitelabel error page or JSON message.

Expected Behavior

Exception is not throwing. User see Whitelabel page or JSON message with status code 403 instead of page with stacktrace and status code 500

Configuration

Spring security Java config:

@EnableWebSecurity
@Configuration
public class DefaultSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
        http
                .requiresChannel()
                    .requestMatchers(request -> request.getHeader("X-Forwarded-Proto") != null)
                        .requiresSecure()
                    .and()
                .cors()
                    .and()
                .headers()
                    .frameOptions()
                    .sameOrigin()
                    .and()
                .csrf()
                    .ignoringAntMatchers("/api/v1/auth/local/user/login", "/api/v1/auth/local/user/registrations")
                    .and()
                .logout()
                    .logoutUrl("/api/v1/auth/logout")
                    .logoutSuccessHandler(new RestLogoutHandler())
                    .clearAuthentication(true)
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID")
                    .and()
                .exceptionHandling()
                    .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
                    .and()
                .authorizeRequests()
                    .antMatchers("/api/v1/auth/social/**")
                        .permitAll()
                    .antMatchers("/api/v1/auth/local/**")
                        .permitAll()
                    .antMatchers("/static/**")
                        .permitAll()
                    .antMatchers("/web/**")
                        .permitAll()
                    .anyRequest()
                        .authenticated();
    }
}

Session or Spring security related properties:

server.servlet.session.timeout=604800
server.servlet.session.cookie.max-age=604800
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true
spring.session.store-type=redis
spring.session.redis.flush-mode=on-save
spring.session.redis.namespace=spring:session

Version

Version: 5.1.2.RELEASE
Problem exists also in: 5.0.8.RELEASE

Sample

core backported bug

All 14 comments

I've performed some more tests and I realized, that problem occurs only when embedded server is Jetty:

compile ('org.springframework.boot:spring-boot-starter-web:2.1.1.RELEASE') {
        exclude module: 'spring-boot-starter-tomcat'
    }
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-jetty', version: '2.1.1.RELEASE'

When I switched to Tomcat, no exception is throwing

Thanks for the report @PawelJ-PL! Can you please provide a complete sample?

I have prepared example app: https://github.com/PawelJ-PL/spring-security-csrf-error
How to reproduce error:

1) Build and run app (because of spring-session, it requires Redis listening on localhost. I've used Redis docker image)
2) In web browser go to endpoint /do/something (GET request)
3) You will be redirected to login page. Enter username: user and password printed in console
4) You will be redirected to page with content QWERTY. Copy SESSION cookie
5) Using curl (or similar tool) send POST request to endpoint /do/something (and use cookie from previous step)
6) You will see stacktrace in response (because CSRF token has not been provided).

Thanks for the sample and the steps to reproduce. It seems like it might be related to a strange behavior in Jetty. Tomcat works just fine. Here is what is happening in Jetty when the request comes in for a POST:

  • Spring Security processes the request with SecurityContextPersistenceFilter which wraps the request (as expected) and marks it as processed
  • In CsrfFilter the request is rejected and it is handled via the error dispatcher.
  • The request comes in again to the error dispatcher

    • It reaches SessionRepositoryFilter which is not invoked because the request attribute is still present. On tomcat, the attribute is not present so it is invoked again and the HttpSession is available

    • The request reaches SecurityContextPersistenceFilter which does not wrap the request again because the attribute is still set.

  • The request comes into the SessionManagementFilter which does not see the session (HttpServletRequest.getSession(false) returns false because SessionRepositoryFilter was not invoked). Since the session is not found it tries to create it, but it has not been wrapped.

The problem is due to the fact that once per request filters are relying on the attributes to determine if they should wrap the request. However, in Jetty on an error dispatch the request is unwrapped but the HttpServletRequest attributes are not reset nor is the finally block of the Filter invoked (which clears the attributes). The specification is clear that the request should not be wrapped on an error dispatch, but it is not clear if the request attributes should be reset or if the finally block should be invoked.

Working Around the Issue

In the meantime I sent a PR to work around the issue. In short, it is a Filter that removes the attribute so SessionRepositoryFilter is invoked again.

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.util.Set;

/**
 * @author Rob Winch
 */
public class RemoveRequestAttrFilter implements Filter {
    private final Set<String> attrNames;

    public RemoveRequestAttrFilter(Set<String> attrNames) {
        this.attrNames = attrNames;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        for (String attrName : this.attrNames) {
            request.removeAttribute(attrName);
        }

        chain.doFilter(request, response);
    }
}

Then add this config

    @Bean
    FilterRegistrationBean<RemoveRequestAttrFilter> removeRequestAttrFilterFilterRegistrationBean() {
        FilterRegistrationBean<RemoveRequestAttrFilter> result = new FilterRegistrationBean<>();
        result.setFilter(new RemoveRequestAttrFilter(Collections.singleton(SessionRepositoryFilter.class.getName().concat(SessionRepositoryFilter.ALREADY_FILTERED_SUFFIX))));
        result.setDispatcherTypes(DispatcherType.ERROR);
        result.setOrder(Ordered.HIGHEST_PRECEDENCE)
        return result;
    }

This appears to be a bug in Spring Session, so I'm transferring the issue there.

Many thanks for the workaround @rwinch. It works for me, but only if I make sure the RemoveRequestAttrFilter is placed before the SessionRepositoryFilter in the filter chain, e.g. by using result.setOrder(Ordered.HIGHEST_PRECEDENCE).

With spring-projects/spring-framework#22989 now resolved, I guess we can apply Rossen's change to our own OncePerRequestFilter.

I did that in vpavic/spring-session@e68a868 and verified that local build of Spring Session works against @PawelJ-PL's sample project.

Is there anything else we should do here @rwinch?

@vpavic The change looks good to me. I think we should consider creating a backport as well.

I think we should consider creating a backport as well.

Yes, I planned to backport this to 2.1.x. I assume we don't backport to 1.3.x since the equivalent change in Spring Framework was made only for 5.1.x, right?

Unfortunately the fix for https://github.com/spring-projects/spring-framework/issues/22989 was shortsighted and caused a regression. The change has been reversed and replaced with a new doFilterNestedErrorDispatch method that needs to be implemented to ensure request and/or response wrapping.

That means there is a need for a follow-up change but I wasn't sure if I should create a ticket here or in Spring Security (or both) so I'm just leaving a comment for the need for a follow-up.

Thanks for the ping @rstoyanchev. I've opened #1470 right after spring-projects/spring-framework#23196 popped out and will apply your latest change to Spring Session's OncePerRequestFilter.

Ok good, I didn't notice that one.

I am facing similar issue but in async calls. My user is getting logged out of the app when I execute a multi threaded task which access SecurityContext or LocaleContext. The session of logged in user gets cleared.

Was this page helpful?
0 / 5 - 0 ratings