Describe the bug
After updating from spring boot 2.6.11 to spring boot 2.7.3 I noticed there was JSESSIONID in the http response headers.
The sample project uses oauth2 resource server and SessionCreationPolicy is configured with STATELESS.
Security Configuration:
@EnableWebSecurity
@Import(OAuth2ResourceServerAutoConfiguration::class)
class DemoSecurityConfiguration {
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
http
.csrf().disable()
.formLogin().disable()
.logout().disable()
.jee().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
http {
authorizeRequests {
authorize(anyRequest, denyAll)
}
}
return http.build()
}
}
It seems the session is created by the org.springframework.security.web.savedrequest.HttpSessionRequestCache
Making an HTTP GET Request via curl
curl -X GET http://localhost:8080/foo -I
HTTP/1.1 401 Unauthorized
Expires: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Set-Cookie: JSESSIONID=-rmUZANCfe7u-NSODewS2lwYOzKKqOQ2YkOgB7h7; path=/
X-XSS-Protection: 1; mode=block
Pragma: no-cache
X-Frame-Options: DENY
Date: Sun, 21 Aug 2022 09:56:14 GMT
Connection: keep-alive
WWW-Authenticate: Bearer
X-Content-Type-Options: nosniff
Content-Length: 0
=> Set-Cookie: JSESSIONID=-rmUZANCfe7u-NSODewS2lwYOzKKqOQ2YkOgB7h7; path=/
Output from the application log:
11:56:14.069 INFO [XNIO-1 task-1] o.s.web.servlet.DispatcherServlet - Completed initialization in 1 ms
11:56:14.084 TRACE [XNIO-1 task-1] o.s.s.w.s.HttpSessionRequestCache - No saved request
11:56:14.095 DEBUG [XNIO-1 task-1] o.s.s.w.a.i.FilterSecurityInterceptor - Failed to authorize filter invocation [GET /foo] with attributes [authenticated]
11:56:14.105 DEBUG [XNIO-1 task-1] o.s.s.w.s.HttpSessionRequestCache - Saved request http://localhost:8080/foo to session
To Reproduce You can easily reproduce with mentioned sample project below.
Expected behavior
No session is created and no JSESSIONID as part of response http headers .
Sample https://github.com/sebastianfuss/spring-boot-session-creation-demo
Comment From: sjohnr
Hi @sebastianfuss, thanks for reaching out and thanks for the sample!
When testing your sample, I am not able to see a behavior difference between Spring Boot 2.6.11 and 2.7.3. It appears the behavior is the same in both releases. Can you confirm?
The issue itself appears to be related to Spring Boot auto-configuration. The presence of the dependency
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
seems to trigger the behavior. Running the configuration with trace logging enabled for Spring Boot, I see the following log messages:
11:35:53.074 TRACE [main] o.s.b.a.s.DefaultWebSecurityCondition - Condition DefaultWebSecurityCondition on org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration$OAuth2SecurityFilterChainConfiguration matched due to AllNestedConditions 2 matched 0 did not; NestedCondition on DefaultWebSecurityCondition.Beans @ConditionalOnMissingBean (types: org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter,org.springframework.security.web.SecurityFilterChain; SearchStrategy: all) did not find any beans; NestedCondition on DefaultWebSecurityCondition.Classes @ConditionalOnClass found required classes 'org.springframework.security.web.SecurityFilterChain', 'org.springframework.security.config.annotation.web.builders.HttpSecurity'
11:35:53.076 TRACE [main] o.s.b.a.condition.OnBeanCondition - Condition OnBeanCondition on org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwtConfiguration$OAuth2SecurityFilterChainConfiguration#jwtSecurityFilterChain matched due to @ConditionalOnBean (types: org.springframework.security.oauth2.jwt.JwtDecoder; SearchStrategy: all) found bean 'jwtDecoderByJwkKeySetUri'
Performing your provided request with trace logging enabled for Spring Security, I see the following log messages:
11:47:06.907 TRACE [XNIO-1 task-1] o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@798deee8, org.springframework.security.web.context.SecurityContextPersistenceFilter@49c099b, org.springframework.security.web.header.HeaderWriterFilter@7eeb38b2, org.springframework.security.web.csrf.CsrfFilter@5f726750, org.springframework.security.web.authentication.logout.LogoutFilter@93501be, org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter@383cb5ce, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@1953bc95, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@30aec673, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@96897c8, org.springframework.security.web.session.SessionManagementFilter@19b07407, org.springframework.security.web.access.ExceptionTranslationFilter@4d98e41b, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@46320c9a]] (1/2)
11:47:06.907 DEBUG [XNIO-1 task-1] o.s.security.web.FilterChainProxy - Securing GET /foo
This seems to confirm that there is an additional filter chain being provided by Spring Boot auto-configuration, and is either an issue either with ordering of auto-configurations or something else related to the @ConditionalOnDefaultWebSecurity annotation.
Assuming the above is correct, the reason a JSESSIONID is included in the response is because enabling the .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) (as Spring Boot does in auto-configuration) doesn't actually disable session management automatically, so the Spring Boot auto-configured filter chain will include it by default. I'm not quite clear on why Spring Boot is creating a filter chain in this sample, as I would expect the auto-configuration to back off.
I'll open an issue with the boot team and see if they have any insight into the issue.
Comment From: sjohnr
@sebastianfuss I also just noticed that you include
@Import(OAuth2ResourceServerAutoConfiguration::class)
at the top of your configuration. This seems to trigger the creation of the SecurityFilterChain that is causing the issue. Is there a reason your sample includes this import? In my testing, the issue goes away when commenting out that import.
Comment From: sebastianfuss
Hi @sjohnr, thanks for your answers.
When testing your sample, I am not able to see a behavior difference between Spring Boot 2.6.11 and 2.7.3. It appears the behavior is the same in both releases. Can you confirm?
Yes. I can confirm that. Sorry for that!
The behaviour changed with switching from extending WebSecurityConfigurerAdapter to SecurityFilterChain, described here.
I extended the sample with both variants, just uncomment the deprecated variant WebSecurityConfigurerAdapter and no session id is there.
@Import(OAuth2ResourceServerAutoConfiguration::class)
In our production services is this necessary, because we are using oauth 2 with jwk setup.
The reason why it is manually configured at DemoSecurityConfiguration to being able to disable security mechanisms - only for testing. (found no better way)
@EnableWebSecurity
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true,
)
@ConditionalOnWebApplication
@ConditionalOnProperty(
prefix = "web.security",
name = ["enabled"],
havingValue = "true",
matchIfMissing = true,
)
@Import(OAuth2ResourceServerAutoConfiguration::class)
class DemoSecurityConfiguration {
I extended the sample as well. Note at the additionally exclusion of the default boot default autoconfiguration classes.
@SpringBootApplication(
exclude = [
WebSocketServletAutoConfiguration::class,
OAuth2ResourceServerAutoConfiguration::class,
SecurityAutoConfiguration::class,
UserDetailsServiceAutoConfiguration::class,
],
)
class DemoApplication
Comment From: sjohnr
Hi @sebastianfuss.
kotlin @Import(OAuth2ResourceServerAutoConfiguration::class)In our production services is this necessary
What I would have expected to happen is the Spring Boot auto-configuration that provides a SecurityFilterChain @Bean to back off and use yours instead. However, because you're importing it explicitly, it is always defined first (as far as I can tell). It also happens to be the first filter chain hit, so requests are not being processed by the filter chain defined in your sample application at all (again, as far as I can tell). Thus your STATELESS configuration is not taking effect. I don't believe this is what you want.
So I believe that as it currently sits, your sample application (and possibly production application) are not working quite the way you expect them to due to mis-configuration.
If you'd like to enable different security configurations conditionally, you may consider using the @Profile annotation instead. For example:
@Configuration
@EnableWebSecurity
class SecurityConfiguration {
@Configuration
@Profile("demo")
class DemoSecurityConfiguration {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
// ...
return http.build()
}
}
@Configuration
@Profile("default")
class DefaultSecurityConfiguration {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
// ...
return http.build()
}
}
}
If you're trying to conditionally disable Spring Boot's auto-configuration, you can provide an @Bean that causes the desired one to back off, in this case the SecurityFilterChain bean, for just the desired profile (e.g. demo) and not provide any bean in the default case.
Can you confirm that I'm understanding correctly what you're trying to do? Or am I misunderstanding something (which is quite possible)?
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: sebastianfuss
Hi @sjohnr,
thank you for your answer and explanation.
So I believe that as it currently sits, your sample application (and possibly production application) are not working quite the way you expect them to due to mis-configuration.
Yes. It does not work as expected after switching to the SecurityFilterChain.
(We sill using the deprecated WebSecurityConfigurerAdapter in production, the configuration works as expected, so no problem here.)
Thank you for your alternative solution to disable spring security optionally. I will change our boot configuration.