spring-boottomcatcookiesspring-webflowjsessionid

Unable to Remove ;jsessionid in a Spring Boot / Web Flow Application's URL when Deployed to Tomcat 8.5


I'm working on a Java application where a user registers a password for his/her account. The following are being used:


For the Spring Security part, there's really no authentication required. I just use it to handle CSRF and the configuration is as follows:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
      // CSRF feature only
      http.authorizeRequests().anyRequest().permitAll();
   }

}

Now, this is where things get messy. When I deploy it to Tomcat in a Unix environment, ;jsessionid gets appended to the URL and Spring Security is not happy. I have scrounged the Internet and found the following solutions to remove it (alongside my results).


server.servlet.session.tracking-modes=cookie in application.properties does nothing.


web.xml

<session-config> 
   <tracking-mode>COOKIE</tracking-mode> 
</session-config>

or

@Configuration
public class WebConfig implements WebApplicationInitializer {

   @Override
   public void onStartup(ServletContext servletContext) {
      HashSet<SessionTrackingMode> set = new HashSet<>();
      set.add(SessionTrackingMode.COOKIE);
      servletContext.setSessionTrackingModes(set);
   }

}

yields an IllegalArgumentException: The session tracking mode [COOKIE] requested for context [/<context-name>] is not supported by that context


I'm about to pull what remains of my hair off so I reverted any cookie-related changes and thought of just allowing semicolons in the URL (I know, I know, not secure) using the snippet below in the same SecurityConfig class.

   @Bean
   public HttpFirewall allowUrlSemicolonHttpFirewall() {
      StrictHttpFirewall firewall = new StrictHttpFirewall();
      firewall.setAllowSemicolon(true);
      return firewall;
   }

   @Override
   public void configure(WebSecurity web) throws Exception {
      super.configure(web);
      web.httpFirewall(allowUrlSemicolonHttpFirewall());
   }

And voila! The web flow runs on an infinite redirect.


Questions:


Please note that allowing semicolons in the URL is working fine and dandy in my localhost, so I have a hunch that what's causing the redirects is SSL-related. In the same way that locally ;jsessionid is not being appended to the URL.

My next step is to try configuring SSL locally in an attempt to replicate the issue. In the meantime, any help would be highly appreciated. My apologies if there's too much information here; I'm willing to repost it as multiple questions if that's necessary.


Solution

  • Answering this one myself as further development this year actually led to a solution from a teammate of mine when he extended the base code to a new application which required Spring Security. Turns out the persistent jsessionid in our case was due to the cookies attribute below in our Tomcat context.xml.

    <Context useHttpOnly="true" cookies="false" reloadable="false">
    

    On the Java side, the generic security configuration below (for the original application) worked even with the Tomcat configuration above.

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.web.SecurityFilterChain;
    
    @Configuration
    @EnableWebSecurity(debug = true)
    @ConditionalOnExpression("${permit.all:true}")
    public class SecurityConfig {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class);
    
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http.authorizeHttpRequests(authorize -> authorize.antMatchers("/**").permitAll())
                    .csrf(httpSecurityCsrfConfigurer -> httpSecurityCsrfConfigurer.ignoringAntMatchers("/**"))
                    .sessionManagement().enableSessionUrlRewriting(true)
                    .sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
    
            LOGGER.debug("Default {} loaded", SecurityConfig.class.getSimpleName());
            return http.build();
        }
    
        @Bean
        public WebSecurityCustomizer webSecurityCustomizer() {
            DefaultHttpFirewall firewall = new DefaultHttpFirewall();
            return web -> web.httpFirewall(firewall);
        }
    
    }