If you're working on a Spring Security (and especially an OAuth) implementation, definitely have a look at the Learn Spring Security course:
>> LEARN SPRING SECURITYMocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
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.
