Spring Boot 3 with Spring Security Intercepts exceptions I don't want it to
Asked Answered
G

2

6

I'm building an API using Spring Boot 3.0.2 with Spring Security, I've built out the security filter chain and it's working fine as far as blocking unauthenticated requests. But I have a RestController class that accepts application/json, and if I don't supply the content type header correctly I want the 415 status code returned. Which gets returned fine without Spring Security in the way. But with Spring Security it seems it's being intercepted and returning a 403 instead.

WebSecurityConfig:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
               .cors().and()
               .csrf().disable()
               .authorizeHttpRequests(auth -> auth
                   .requestMatchers("/auth**").permitAll()
                   .anyRequest().authenticated())
               .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
               .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
               .userDetailsService(jpaUserDetailsService)
               .build();
}

Auth Filter:

@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
    private final JwtUtils jwtUtils;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String jwtToken = request.getHeader(AUTHENTICATION_HEADER);

        if (jwtToken != null && jwtToken.startsWith("Bearer ")) {
            jwtToken = jwtToken.split(" ")[1];
            if (jwtUtils.isValidToken(jwtToken)) {
                UserDetails userDetails = new UserSecurity(jwtUtils.extractUser(jwtToken));
                UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails,
                        null, userDetails.getAuthorities());
                auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }

        filterChain.doFilter(request, response);
    }
}

Rest Controller:

@RestController
@RequestMapping(value = "auth", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public class AuthController {
    @GetMapping
    public Object test() {
        Map<String, String> test = new HashMap<>();
        test.put("key", "val");

        return test;
    }
}

Is there a way to only return the 403 exception if it actually is an unauthenticated error? Not for every single exception?

Gourley answered 29/1, 2023 at 0:52 Comment(2)
You have to permit the URL /error to see the error.Foothold
Wow ok, such a small change, but you're correct, adding /error to the permit all worked. Thank you! Do you want answer the question so I can choose it as the answer?Gourley
F
11

You have to open the error endpoint, see Spring Boot 2.0 Migration Guide:

Default Security

The security auto-configuration no longer exposes options and uses Spring Security defaults as much as possible. One noticeable side effect of that is the use of Spring Security’s content negotiation for authorization (form login).

Spring Boot 2.0 doesn’t deviate too much from Spring Security’s defaults, as a result of which some of the endpoints that bypassed Spring Security in Spring Boot 1.5 are now secure by default. These include the error endpoint and paths to static resources such as /css/**, /js/**, /images/**, /webjars/**, /**/favicon.ico. If you want to open these up, you need to explicitly configure that.

The error endpoint renders the response, see Spring Boot Reference:

27.1.11 Error Handling

By default, Spring Boot provides an /error mapping that handles all errors in a sensible way, and it is registered as a “global” error page in the servlet container. For machine clients, it produces a JSON response with details of the error, the HTTP status, and the exception message. For browser clients, there is a “whitelabel” error view that renders the same data in HTML format (to customize it, add a View that resolves to error).

Your modified security chain configuration:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
               .cors().and()
               .csrf().disable()
               .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/error").permitAll()
                    .requestMatchers("/auth**").permitAll()
                    .anyRequest().authenticated())
               .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
               .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
               .userDetailsService(jpaUserDetailsService)
               .build();
}
Foothold answered 30/1, 2023 at 10:2 Comment(2)
@ILyaCyclone Yes, it is, I couldn't find any newer documentation.Foothold
Shame that it's not mentioned explicitlyDoorknob
S
3

You have to permit the URL /error to see the error.

Sulcate answered 29/1, 2023 at 0:53 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.