Spring Session Redis not replacing Tomcat JSESSIONID (two session cookies)

1 day ago 3
ARTICLE AD BOX

I am developing a BFF (Backend For Frontend) service using Spring. For authentication, I am using an Identity Provider (Keycloak for local development). I am using the Authorization Code Flow via the spring-boot-starter-oauth2-client library. For session storage, I decided to use Redis, so I added spring-session-data-redis.

Everything is working fine — sessions are being stored in Redis correctly. However, after signing in, I see both JSESSIONID and SESSION cookies in my browser. In my case, I believe I don’t need the JSESSIONID cookie anymore since I switched to Spring Session.

Photo with cookies in browser

As I understand it, JSESSIONID is generated by Apache Tomcat, while SESSION is generated by Spring Session. I tried debugging by checking all request filters, but I still don’t understand how to prevent the creation of the JSESSIONID cookie.

So my question is: how can I prevent the JSESSIONID cookie from being generated?

Below I am providing my SecurityConfig.java, build.gradle, and application.yaml. In case you need more details regarding my project setup, let me know.

I would appreciate any help, as well as links to articles or documentation that explain this in more detail.

P.S. I found a similar question here: Spring Sessions HttpSession unable to completely replace JSESSIONID, but I’m not sure if the answers there reflect best practices. Also i tried few of them and they didn't work for me.

P.P.S. I checked this article as well: spring-session-data-redis not working, but it also has answers for an .xml based configuration. I tried to order filters using: spring:session:servlet:filter-order: -2147483648, but it didn't help as well.

dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.session:spring-session-data-redis' implementation 'org.springframework.boot:spring-boot-starter-webmvc' testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } spring: security: oauth2: client: registration: shelfio: clientId: ${IDP_CLIENT_ID} clientSecret: ${IDP_CLIENT_SECRET} scope: openid redirect-uri: ${IDP_REDIRECT_URI} provider: shelfio: issuerUri: ${IDP_ISSUER_URI} session: store-type: redis timeout: 30m redis: namespace: shelfio:bff:session data: redis: host: ${REDIS_HOST} port: ${REDIS_PORT} password: ${REDIS_PASSWORD} logging: level: org.springframework.security: DEBUG org.springframework.security.oauth2: DEBUG org.springframework.security.oauth2.client: DEBUG org.springframework.web.filter.CommonsRequestLoggingFilter: DEBUG package org.shelfio.bff.config; import jakarta.servlet.http.HttpServletResponse; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; @Configuration @EnableRedisHttpSession public class SecurityConfig { /** * CORS and CSRF implementation will be provided later. */ @Bean public SecurityFilterChain filterChain(HttpSecurity http) { http.csrf(Customizer.withDefaults()) .authorizeHttpRequests(authorize -> authorize .requestMatchers("/oauth2/**", "/login/oauth2/**").permitAll() .anyRequest().authenticated()) .oauth2Login(Customizer.withDefaults()) .logout(logout -> logout .deleteCookies("SESSION") .logoutSuccessHandler((_, res, _) -> { res.setStatus(HttpServletResponse.SC_OK); })) .sessionManagement(session -> session .sessionFixation(SessionManagementConfigurer.SessionFixationConfigurer::migrateSession)); return http.build(); } }
Read Entire Article