Mastering Custom Exception Handling in Spring Security (Spring Boot 2.4)
This guide explains how Spring Security processes authentication and authorization failures, walks through the default exception flow, and demonstrates multiple ways to customize error handling—including custom failure handlers, access‑denied pages, and JSON responses—using Spring Boot 2.4.12.
Default Exception Mechanism
When a request lacks the required permission, Spring Security returns a default error page. For example, an incorrect username or password triggers a BadCredentialsException , while insufficient roles cause an AccessDeniedException . The framework processes these exceptions through a chain of filters and handlers.
The core filter handling login submissions is UsernamePasswordAuthenticationFilter , which delegates authentication to ProviderManager . The manager iterates over configured AuthenticationProvider implementations; if none succeed, the last caught exception is re‑thrown.
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("guest").password("123456").roles("ADMIN")
.and()
.withUser("test").password("666666").roles("USERS");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/resources/**", "/cache/**").permitAll();
http.authorizeRequests().antMatchers("/demos/**").hasRole("USERS");
http.authorizeRequests().antMatchers("/api/**").hasRole("ADMIN");
http.formLogin().loginPage("/custom/login");
}
}During authentication, UsernamePasswordAuthenticationFilter#attemptAuthentication calls ProviderManager#authenticate , which invokes each AuthenticationProvider . If the username is not found, a BadCredentialsException is thrown. The exception propagates back to the filter, where SimpleUrlAuthenticationFailureHandler stores it in the session and redirects to the login page with an error parameter.
For authorization failures, FilterSecurityInterceptor#invoke eventually calls AbstractSecurityInterceptor#attemptAuthorization . The AffirmativeBased decision manager evaluates voters; if any voter denies, an AccessDeniedException is thrown. This exception is caught by ExceptionTranslationFilter , which delegates to an AccessDeniedHandler . By default, the handler sends a 403 response.
Custom Exception Configuration
To customize authentication failure responses, you can provide a custom AuthenticationFailureHandler that returns JSON instead of a redirect:
http.formLogin()
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("{\"code\": -1, \"message\": \"" + exception.getMessage() + "\"}");
out.close();
}
})
.loginPage("/custom/login");You can also expose this handler as a Spring bean for reuse.
For authorization failures, you have three options:
Create a custom HTML page (e.g., denied.html) and map it with http.exceptionHandling().accessDeniedPage("/access/denied"). A simple controller returns the view name.
Define a custom 403 page (e.g., 403.html) and let Spring serve it when accessDeniedPage is not set.
Implement a custom AccessDeniedHandler that writes a JSON payload, similar to the authentication failure handler:
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException ex) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("{\"code\": -1, \"message\": \"" + ex.getMessage() + "\"}");
out.close();
}
});Testing each configuration shows the corresponding JSON or HTML response when a user with insufficient permissions accesses /demos/** (requires role USERS ) or /api/** (requires role ADMIN ).
In summary, you now understand:
The internal flow of authentication‑failure handling and how to replace the default redirect with a JSON response.
The internal flow of authorization‑failure handling and how to customize the access‑denied response via HTML pages or JSON handlers.
The next article will cover the core filter creation principles and how to implement custom filters in Spring Security.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
