Describe the bug A clear and concise description of what the bug is.

Even after configuring authorizeHttpRequests to permit all requests for /js/ and /css/, and setting headers(headers -> headers.cacheControl(Customizer.withDefaults())), the JavaScript (js) and CSS files are still not being cached and fetched from the server every time.

To Reproduce Steps to reproduce the behavior. Open Expected behavior A clear and concise description of what you expected to happen. 1. When opening the page and log in. 2.Each time, the response headers indicate "no-cache, no-store, max-age=0, must-revalidate" for the JS and CSS files.

Sample

A link to a GitHub repository with a minimal, reproducible sample. spring-security-core 5.3.9.RELEASE is OK! Spring Security The Cache-Control setting in Spring Security 6 is not taking effect.

spring-security-core 6.0.1 is error! Spring Security The Cache-Control setting in Spring Security 6 is not taking effect.

Reports that include a sample will take priority over reports that do not. At times, we may require a sample, so it is good to try and include a sample up front.

Comment From: jzheaux

Hi, @bu6030. By default, Spring Security configures responses so that contents are not cached.

Will you please share what you changed when migrating from 5.3 to 6.0? Perhaps in 5.3 you were using webSecurity.ignoring to ignore those endpoints and now you are using permitAll instead.

Comment From: bu6030

Hi, @jzheaux. Yes, I used webSecurity.ignoring to ignore those static files and the contents are cached. Is there any code to replace webSecurity.ignoring in 6.0? The code 5.3:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.sql.DataSource;

import java.time.Duration;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {

    @Autowired
    DataSource dataSource;
    private final static String ACCOUNT_CLIENT_AUTHORITY = "ADMIN";

    @Override
    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // ignore
        web.ignoring().antMatchers("/css/**", "/js/**", "/fonts/**", "/images/**",
            "/i/**", "/resources/**");
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedHeaders("*")
            .allowedMethods("*")
            .maxAge(1800)
            .allowedOrigins("*");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
            .addResourceLocations("classpath:/static/")
            .setCacheControl(CacheControl.maxAge(Duration.ofDays(1)));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/login", "/chrome/**", "/css/**", "/js/**")
            .permitAll()
            .and()
            .authorizeRequests()
            .anyRequest()
            .hasAuthority(ACCOUNT_CLIENT_AUTHORITY)
            .and()
            .csrf()
            .disable()
            .sessionManagement()
            .disable()
            .cors()
            .and()
            .headers().cacheControl()
            .and()
            .contentTypeOptions();
        http.formLogin(withDefaults());
    }
}

Comment From: jzheaux

What I'd recommend is handling your static resources in a separate filter chain like so:

@Bean 
@Order(0)
SecurityFilterChain staticEndpoints(HttpSecurity http) throws Exception {
    http
        .securityMatcher("/css/**", "/js/**", "/fonts/**", "/images/**", "/i/**", "/resources/**")
        .headers((headers) -> headers.cacheControl((cache) -> cache.disable()))
        .authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll());
    return http.build();
}

What this bean is saying is that the listed rules only apply to the URIs specified in securityMatcher.

This will go alongside a second bean definition for the rest of your apps endpoints:

@Bean 
SecurityFilterChain appEndpoints(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .requestMatchers("/login", "/chrome/**").permitAll()
            .anyRequest().hasAuthority(ACCOUNT_CLIENT_AUTHORITY)
        )
        .formLogin(Customizer.withDefaults())
        // .. the rest of your existing configuration
    return http.build();
}