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

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 SECURITY

1. Introduction

In this article, we will show how to customize the access denied page in a Spring Security project.

This can be achieved either through the Spring Security configuration or web application configuration in the web.xml file.

In the remaining sections, we will take a more in-depth look at each of these options.

2. Custom JSP

Whenever a user attempts to access a page that is restricted to roles they do not have, the application will return a status code of 403, which means Access Denied.

In order to replace the Spring 403 status response page with a custom one, let’s first create a JSP file called accessDenied.jsp:

<body>
<h2>Sorry, you do not have permission to view this page.</h2>

Click <a href="<c:url value="/homepage.html" /> ">here</a>
to go back to the Homepage.
</body>

3. Spring Security Configuration

By default, Spring Security has an ExceptionTranslationFilter defined which handles exceptions of type AuthenticationException and AccessDeniedException. The latter is done through a property called accessDeniedHandler, which uses the AccessDeniedHandlerImpl class.

In order to customize this behavior to use our own page that we created above, we need to override the properties of the ExceptionTranslationFilter class. This can be done through either Java configuration or XML configuration.

3.1. Access Denied Page

Using Java, we can customize the 403 error handling process by using the accessDeniedPage() or accessDeniedHandler() methods while configuring the HttpSecurity element.

Let’s create an authentication configuration that restricts the “/admin/**” URLs to the ADMIN role and sets the access denied page to our custom accessDenied.jsp page:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
      // ...
      .and()
      .exceptionHandling().accessDeniedPage("/accessDenied.jsp");
}

Let’s take a look at the equivalent XML configuration for the access denied page:

<http use-expressions="true">
    <access-denied-handler error-page="/accessDenied"/>
 </http>

3.2. Access Denied Handler

Using an access denied handler instead of a page has the advantage that we can define custom logic to be executed before redirecting to the 403 page. For this, we need to create a class that implements the AccessDeniedHandler interface and overrides the handle() method.

Let’s create a custom AccessDeniedHandler class that logs a warning message for every access denied attempt containing the user that made the attempt and the protected URL they were trying to access:

public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    public static final Logger LOG
      = Logger.getLogger(CustomAccessDeniedHandler.class);

    @Override
    public void handle(
      HttpServletRequest request,
      HttpServletResponse response, 
      AccessDeniedException exc) throws IOException, ServletException {
        
        Authentication auth 
          = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null) {
            LOG.warn("User: " + auth.getName() 
              + " attempted to access the protected URL: "
              + request.getRequestURI());
        }

        response.sendRedirect(request.getContextPath() + "/accessDenied");
    }
}

In the security configuration, we’ll define the bean and set the custom AccessDeniedHandler:

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

//...
.exceptionHandling().accessDeniedHandler(accessDeniedHandler());

If we want to configure the CustomAccessDeniedHandler class defined above using XML, the configuration will look slightly different:

<bean name="customAccessDeniedHandler" 
  class="com.baeldung.security.CustomAccessDeniedHandler" />

<http use-expressions="true">
    <access-denied-handler ref="customAccessDeniedHandler"/>
</http>

4. Application Configuration

Handling the access denied error can be done through the web.xml file of a web application, by defining an error-page tag. This contains two subtags called error-code, which specifies the status code to be intercepted, and location, which signifies the URL to which the user will be redirected in case the error code is encountered:

<error-page>
    <error-code>403</error-code>
    <location>/accessDenied</location>
</error-page>

If an application does not have a web.xml file, as is the case with Spring Boot, the Spring annotations do not currently provide an exact alternative to the error-page tag. According to the Spring documentation, in this case, the recommended approach is to use the methods accessDeniedPage() and accessDeniedHandler() presented in section 3.

5. Conclusion

In this quick article, we have detailed the various ways that an access denied error can be handled using a custom 403 page.

The complete source code of the article can be found in the GitHub project.

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 open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.