Home > Enterprise >  change Spring Security to return 401 when authentication fails
change Spring Security to return 401 when authentication fails

Time:06-29

In my Spring Boot (v. 2.7.0) app, I'm using Spring Security for authentication. If a user attempts to login with invalid credentials, the server responds with a 403 (Forbidden) status code, but I would expect 401 (Unauthorized) to be used instead.

I can't find anything in my configuration that indicates the default behaviour of Spring Security has been overriden, but whether it has or not, I want 401 to be returned when authentication fails.

I stepped through the relevant Spring Security code and it appears the Spring Security method SimpleUrlAuthenticationFailureHandler.onAuthenticationFailure is called when authentication fails. This method includes the line

response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());

But for some reason 403 is returned to the client - so I guess the response is being changed subsequent to the line above.

How can I change Spring Security to return 401 when authentication fails? I've included my security configuration below for reference.

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
public class SecurityConfiguration {

    @Autowired
    private AuthenticationConfiguration authenticationConfiguration;

    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();
        var jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager);

        var jwtAuthorisationFilter = new JwtAuthorisationFilter();

        http.cors().and().csrf().disable().authorizeRequests()
            .anyRequest().authenticated().and()
            .addFilter(jwtAuthenticationFilter)
            .addFilterAfter(jwtAuthorisationFilter, BasicAuthenticationFilter.class)
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        return http.build();
    }
}

The JwtAuthenticationFilter calls authenticationManager.authenticate which causes a org.springframework.security.authentication.BadCredentialsException to be thrown if the credentials are invalid.

Update

I tried adding a custom AuthenticationFailureHandler bean as described in this article, but my custom bean is never invoked (the default bean SimpleUrlAuthenticationFailureHandler is called instead).

CodePudding user response:

You can provide your own custom AccessDeniedHandler implementation.

@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();
    var jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager);

    var jwtAuthorisationFilter = new JwtAuthorisationFilter();

    http.cors().and().csrf().disable().authorizeRequests()
        .anyRequest().authenticated().and()
        .addFilter(jwtAuthenticationFilter)
        .addFilterAfter(jwtAuthorisationFilter, BasicAuthenticationFilter.class)
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .exceptionHandling()
        .accessDeniedHandler( (request, response, exception) ->
             response.sendError(HttpStatus.UNAUTHORIZED.value(), exception.getMessage()
        ));

    return http.build();
}
  • Related