1. Download generated code from https://start.spring.io SpringBoot Spring Boot 3: ResponseStatusException does not work

  2. Replace content of DemoApplication.kt with the following code

@SpringBootApplication
class DemoApplication

fun main(vararg args: String) {
  SpringApplication.run(DemoApplication::class.java, *args)
}

@RestController
class HelloController {
  @GetMapping("/public-api/hello")
  fun hello(@RequestParam error: Boolean = false) =
    if (error)
      throw ResponseStatusException(HttpStatus.BAD_REQUEST)
    else
      ResponseEntity.ok().build<Unit>()
}

@Configuration
@EnableWebSecurity
class SecurityConfig {
  @Bean
  fun securityFilterChain(http: HttpSecurity) = http
    .csrf().disable()
    .httpBasic().disable()
    .authorizeHttpRequests { requests ->
      requests
        .requestMatchers("/public-api/**").permitAll()
        .anyRequest().authenticated()
    }
    .build()
}
  1. PROBLEM: Request http://localhost:8080/public-api/hello?error=true returns "403 Forbidden"
  2. Now in build.gradle.kts, change id("org.springframework.boot") version "3.0.2" to id("org.springframework.boot") version "2.7.8"
  3. In DemoApplication.kt, change requestMatchers("/public-api/**") to antMatchers("/public-api/**") because requestMatchers is not available in Spring Boot 2.
  4. CORRECT: Request http://localhost:8080/public-api/hello?error=true returns "400 Bad Request" with body
{
    "timestamp": "2023-01-22T13:39:18.084+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/public-api/hello"
}

Comment From: philwebb

By default Spring Boot 3.0 also applies security to error dispatches. In your example, the .requestMatchers("/public-api/**") filter is working but there's no equivalent rule for permitting errors.

Try adding:

requests.requestMatchers("/error").permitAll();

to your configuration.

Comment From: xuanswe

@philwebb nice. It works. Thanks! So, in Spring 2, /error is allowed regardless of custom authorizeHttpRequests. In Spring 3, if there's a custom authorizeHttpRequests, the default security rules are disabled. Therefore, we need to explicitly redefine them.

Comment From: mbhave

@xuan-nguyen-swe The reason why /error worked in 2.x was because we had a custom filter which checked if a user should be granted access to the error page or not. In Spring Boot 3.0, we removed the custom filter to align with Spring Security's defaults which always apply the security filter to the error dispatch.

One thing to note is that, adding permitAll to the /error page is not quite the same as the getting the behavior from Spring Boot 2.7.x back. permitAll() allows anyone to access the error page while in Spring Boot 2.7, the default was only to allow access to the error page if access to the original request was allowed. It's good to verify that public access to the error page is okay before adding permitAll. I've created an issue in Spring Security to see if this can be fixed there.

Comment From: izeye

I've created an issue in Spring Security to see if this can be fixed there.

It's pointing to a Spring Boot issue. https://github.com/spring-projects/spring-security/issues/12771 seems to be the intended one.

Comment From: mbhave

Thanks @izeye, fixed!