Course – LSS – NPI (cat=Security/Spring Security)
announcement - icon

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security:

>> CHECK OUT THE COURSE

1. Overview

In this article, we’ll have a look at how to handle Spring Security exceptions produced by our Spring Security Resource Server. To do so, we’ll also use a practical example where all the necessary configurations will be explained. First of all, let’s make a short introduction to Spring Security.

2. Spring Security

Spring Security is a library that’s part of the Spring project. It tries to group all the functionalities of user access control on Spring projects. Access control allows limiting the options that can be executed by a given set of users or roles on the application. In this direction, Spring Security controls invocations to business logic or limits the access of HTTP requests to certain URLs. With this in mind, we must configure the application by telling Spring Security how the security layer should behave.

In our case, we’ll focus on the configuration of exception handlers. Spring Security offers three different interfaces to accomplish this purpose and to control the events produced:

  • Authentication Success Handler
  • Authentication Failure Handler
  • Access Denied Handler

Firstly, let’s take a closer look at the configuration.

3. Security Configuration

First of all, we’ve our configuration class that has to create a SecurityFilterChain bean. This will be in charge of managing all the security configurations of the application. So, it’s here where we have to introduce our handlers.

On the one hand, we’ll define the required configuration:


 @Bean
 public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
      http.csrf(AbstractHttpConfigurer::disable)
          .httpBasic(AbstractHttpConfigurer::disable)
          .authorizeHttpRequests(auth -> auth
          .requestMatchers("/login")
          .permitAll()
          .requestMatchers("/customError")
          .permitAll()
          .requestMatchers("/access-denied")
          .permitAll()
          .requestMatchers("/secured")
          .hasRole("ADMIN")
          .anyRequest()
          .authenticated())
          .formLogin(form -> form.failureHandler(authenticationFailureHandler())
                    .successHandler(authenticationSuccessHandler()))
                    .exceptionHandling(ex -> ex.accessDeniedHandler(accessDeniedHandler()))
          .logout(Customizer.withDefaults());
      return http.build();
    }
}

It’s interesting to note that redirection URLs, such as “/login”, “/customError”, and “/access-denied” don’t have to have any type of restriction to access them. So, we annotate them as permitAll().

On the other hand, we’ve to define the Beans that define the types of exceptions that we can handle:

@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
    return new CustomAuthenticationFailureHandler();
} 

@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
   return new CustomAuthenticationSuccessHandler();
}

@Bean
public AccessDeniedHandler accessDeniedHandler() {
   return new CustomAccessDeniedHandler();
}

Since the AuthenticationSuccessHandler handles the happy path, we’ll define the two remaining beans for the exception cases. These two handlers are the ones we now have to adapt and implement to our needs. So, let’s proceed with the implementation of each of them.

4. Authentication Failure Handler

On the one hand, we’ve got the AuthenticationFailureHandler interface. That’s in charge of managing the exceptions produced when the user fails to log in. This interface provides us with the onAuthenticationFailure() method to customize the handler logic. It will be invoked by Spring Security upon a failed login attempt. With this in mind, let’s define our exception handler to redirect us to the error page when a failed login occurs:

public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) 
      throws IOException {
        response.sendRedirect("/customError");
    }
}

5. Access Denied Handler

On the other hand, when an unauthorized user tries to access the secure or protected page, Spring Security will throw an access denied exception. There’s a default 403 access denied page available with Spring Security which we can customize. This is managed by the AccessDeniedHandler interface. In addition, it provides the handle() method for custom the logic before redirecting the user to the 403 page:

public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exc) throws IOException {
        response.sendRedirect("/access-denied");
    }
}

6. Conclusion

In this quick article, we’ve learned how to handle Spring Security exceptions and how to control them by creating and customizing our classes. In addition, we’ve created a fully functional example that helps us with understanding the concepts explained.

The complete source code of the article is available over on GitHub.

Course – LSS (cat=Security/Spring Security)

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security:

>> CHECK OUT THE COURSE
res – Security (video) (cat=Security/Spring Security)
Comments are closed on this article!