HandlerInterceptor is similar to a Servlet Filter. In addition, HandlerInterceptor allows custom pre-processing with the option to prohibit the execution of the handler itself and custom post-processing. Servlet filters are configured in web.xml while HandlerInterceptors in the application context.
In this article, we will build a simple spring web application to demonstrate how the HandlerInterceptor works. Our application will have a un intercepted page and intercepted page where it checks for login credentials.
What is spring handler interceptor?
Spring HandlerInterceptor can intercept requests to your web application. In a spring web application, an interceptor can intercept requests before the request reaches its designated handler method and before the response is sent to the client. The below figure illustrates how this process works in a spring web application.
The figure illustrates two client requests. The first request has no interceptor configured. The second request has an interceptor configured. That goes through the handler interceptor process discussed below.
1. The client issue the request to visit the admin page.
2. The dispatcher servlet receives the request. Since the URL /admin is configured to be intercepted, the request is passed through to the pre-handle method of the handler interceptor. (More on how to map URLs listed below)
3. Pre-handle executes applicable logic and passes the request to the designated destination. In our case, it is the admin controller. The controller method executes the necessary logic and sends the response back to the client.
4. The post-handle method intercepts the response and executes applicable logic to modify the response.
5. The client receives the response and completes the rendering process.
6. The after-completion method executes applicable logic to complete the process.
Now let's build a simple project that demonstrates what we discussed so far. All the steps listed below uses the Eclipse IDE.
How to create spring handler interceptor project?
We will create a simple maven webapp and add spring capabilities and handler interceptor to it.
Creating a simple maven webapp
Right-click project explorer
or
Click Next
Select Maven Project from the wizard
Click Next
Select a project workspace location
Click Next
Select maven-archetype-webapp
Click Next
Provide an artifact id
Click Next
Now your basic web app is ready. If you see any project errors as listed below, follow the instructions provided in Dynamic Web Project with Maven and Eclipse to resolve them.
Adding spring capabilities to maven webapp
Edit the pom.xml and add spring framework-specific dependencies as below.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.learnbestcoding</groupId>
<artifactId>request_interceptors</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>request_interceptors Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
<build>
<finalName>request_interceptors</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Edit the web.xml file and include the Spring DispatcherServlet. The DispatcherServlet received all the client requests as illustrated in the above figure.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>request_interceptors</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>request_interceptors</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Create a spring application context
Create an XML file named applicationContext in the WEB-INF folder. That is where we declare what URLs we want to intercept. More on that later.
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd ">
<mvc:annotation-driven />
<context:component-scan base-package="com.web,com.util" />
<mvc:default-servlet-handler />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
</beans>
Create controller and value object class
Our controller class resides in com.web, and AdminVo resides in com.util packages, respectively.
package com.web;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import com.util.AdminVO;
@Controller
public class AdminController{
@RequestMapping(value = "adminLogin")
public String adminLogin() throws IOException
{
return "adminLogin";
}
@RequestMapping(value = "/intercept/authenticate")
public void authenticate(HttpServletRequest request, HttpServletResponse response, @ModelAttribute AdminVO vo) throws IOException
{
if("LearnBestCoding".equals(vo.getUserName())) {
request.getSession().setAttribute("adminvo", vo);
response.sendRedirect("/request_interceptors/intercept/adminPage");
}
else {
response.sendRedirect("adminLogin");
}
}
@RequestMapping(value = "/intercept/adminPage")
public String adminPage(HttpServletRequest request) throws IOException
{
return "adminPage";
}
@RequestMapping(value = "/logout")
public String logout(HttpServletRequest request) throws IOException
{
request.getSession().removeAttribute("adminvo");
return "adminLogin";
}
}
package com.util;
public class AdminVO {
String userName;
String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isAuthenticated() {
return authenticated;
}
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
boolean authenticated;
}
Create jsp's to render view
We will secure adminPage.jsp by intercepting requests to it. If the user is not authenticated, the user will be redirected to adminLogin.jsp to authenticate. Now create adminPage.jsp and adminLogin.jsp in WEB-INF/jsp folder.
<html>
<body>
<h2>Hello ${adminvo.userName}!</h2>
<table>
<tr><td>${postHandle}</td></tr>
<tr><td>Click <a href='/request_interceptors/logout'>here</a> to log out.</td></tr>
</table>
</body>
</html>
<html>
<body>
<h2>Login Page</h2>
<form action="/request_interceptors/intercept/authenticate" method="POST">
<table>
<tr><td>Enter user name:</td><td><input type="text" name="userName" size="20"></td></tr>
<tr><td>Enter password:</td><td><input type="password" name="password" size="20"></td></tr>
<tr><td colspan="2"><input type="submit" value="Submit" /></td></tr>
</table>
</form>
</body>
</html>
Create HandlerInterceptor class
Out interceptor class name is LinkInterceptor. It resides in the com.util folder. Note that logic in preHandle and postHandle methods alter the request and add additional attributes.
package com.util;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class LinkInterceptor implements HandlerInterceptor{
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView){
request.setAttribute("postHandle", "Message from postHandle method");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(request.getRequestURI().contains("authenticate"))
{
return true;
}
if(request.getRequestURI().contains("adminPage"))
{
if(request.getSession().getAttribute("adminvo") != null)
{
return true;
}
else
{
response.sendRedirect("/request_interceptors/adminLogin");
return false;
}
}
else
{
return true;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
System.out.println("The http status for : " + request.getRequestURL() +" is : "+ response.getStatus());
}
}
How to intercept request URLs with HandlerInterceptor?
In order to intercept URLs, we can use <mvc:interceptors> in the applicationContext.xml. Within the <mvc:interceptors>, you can declare multiple interceptors. Note that <mvc:mapping> is the key to specify the exact URL patterns we want to intercept. Each specified URL pattern is intercepted by the declared interceptor class within the <mvc:interceptor>. In this example, all URLs containing */intercept/* in the URL are intercepted by the LinkInterceptor.
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd ">
<mvc:annotation-driven />
<context:component-scan base-package="com.web,com.util" />
<mvc:default-servlet-handler />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/intercept/**" />
<bean class="com.util.LinkInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
Each intercepted URL will go through preHandle and postHandle methods. The afterCompletion is triggered upon completion of the rendering process.
Example output of our demo webapp with HandlerInterceptor
Our login page and admin page will look like below. Note that the admin page is only accessible once the login credentials are validated.
If you attempt to access the admin page by directly typing the URL http://localhost:8090/request_interceptors/intercept/adminPage, you will notice that the interceptor redirects the user back to the login page.
Download Spring handlerinterceptor example