[JAVA] Use Interceptor in Spring

I learned about interceptor in Spring, so I will write an article.

Interceptor overview

The Interceptor class in Spring is a class used when, for example, "I want to implement a class that performs some common processing before the controller is called". For example, it is used when you want to authenticate the accessing user before mapping the request.

Implementation class

I will briefly explain the implemented class.

Interceptor class

TestInterceptor.java


import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.example.annotation.NonAuth;
import com.example.models.TestUser;
import com.example.service.UserPermissionService;

public class TestInterceptor extends HandlerInterceptorAdapter{

	@Autowired
	UserPermissionService service;

    @Override
    public boolean preHandle(
                    HttpServletRequest request,
                    HttpServletResponse response,
                    Object handler) throws Exception {

    	//False if the request cannot be mapped
    	if( handler instanceof HandlerMethod ) {

    		// @Check if NonAuth is granted
    		HandlerMethod hm = (HandlerMethod) handler;
            Method method = hm.getMethod();
            NonAuth annotation = AnnotationUtils.findAnnotation(method, NonAuth.class);
            if (annotation != null) {
                return true;
            }

            //User authentication
	    	HttpSession session = request.getSession(false);
	    	try {
	    		TestUser user = (TestUser)session.getAttribute("user");
	    		if(!service.checkPermission(user)) {
	    			response.sendRedirect("/error");
	    			return false;
	    		}
	    	}catch(NullPointerException e){
	    		response.sendRedirect("/error");
	    		return false;
	    	}
	        return true;
    	}
    	response.sendRedirect("/error");
    	return false;
    }
}

-The Interceptor class must inherit from HandlerInterceptorAdaptor. -PreHandle () is a method that is called before the controller process is called. You can also override postHandle (), which is called after the controller has finished processing, and afterCompletion (), which is called after a series of request processing is finished. -Returns true if the request is made to a method with NonAuth annotation in the first place (described later). -In the above class, user authentication is performed with UserPermission.checkPermission (), and if the check is hit, the user is redirected to the error screen (described later). -If the passed Handler is not an instance of HandlerMethod, it will redirect to the error screen (described later).

Configuration class

BeanConfiguration.java


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;

import com.example.interceptor.TestInterceptor;
import com.example.service.TestService;

@Configuration
public class BeanConfiguration {

	@Bean
	public HandlerInterceptor testInterceptor() throws Exception{
		return new TestInterceptor();
	}

}

-Define the Interceptor class as a bean.

WebMvcConfig class

WebMvcConfig.java


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

	@Autowired
    HandlerInterceptor testInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(testInterceptor);
    }
}

-Interceptor is a class that inherits WebMvcConfigurer, and it is necessary to make Spring recognize it by addInterceptors (). -Call the Interceptor class with Autowired.

Controller class

TestInterceptorController.java


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.example.annotation.NonAuth;
import com.example.models.TestUser;

@Controller
@RequestMapping("/interceptor")
public class TestInterceptorController {

	@RequestMapping(value="/index",method = RequestMethod.GET)
	@NonAuth
	public String index(Model model, HttpServletRequest request) {

		TestUser user = new TestUser();
		user.setName("Tanaka");

		HttpSession session = request.getSession(false);
		if (session == null){
			session = request.getSession(true);
			session.setAttribute("user", user);
		}else {
			session.setAttribute("user", user);
		}

		return "test/index";
	}

	@RequestMapping(value="/test", method=RequestMethod.GET)
	public String test(Model model) {
		model.addAttribute("msg", "HelloWorld!");
		return "test/test";
	}

}

-First, when you access index, the user is registered in the session. -Since the index has NonAuth annotation, it passes the authentication without any questions. -You must clear the authentication to access test.

That's all for the rough implementation class.

Supplement

If you want a particular method to always return true in preHandle ()

-As mentioned above, there is a method using annotations, for example. -Define the annotation yourself. -We are checking whether the method is annotated as defined in preHandle () of the Interceptor class. ・ If it is given, no questions are asked and true is returned. ・ I referred to the following site. https://qiita.com/dmnlk/items/cce551ce18973f013b36

When you want to transition to the error screen when authentication fails

-If you want to handle errors in Spring, define a class that inherits ErrorController (class prepared in Spring).

ErrorController


import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.annotation.NonAuth;

@Controller
public class ErrController implements ErrorController {

    private static final String PATH = "/error";

    @RequestMapping("/404")
    @NonAuth
    String notFoundError() {
        return "test/error";
    }

    @RequestMapping(PATH)
    @NonAuth
    String home() {
        return "test/error";
    }

    @Override
    @NonAuth
    public String getErrorPath() {
        return PATH;
    }

}

-Since the defined method does not perform authentication processing, add NonAuth annotation. -It seems that return false must be done after dispatching the error screen in the Interceptor class. This is because if you do not write return false, the handler method (mapped controller method) will be started even if the transition to the error screen is performed. I referred to the following site. https://stackoverflow.com/questions/44740332/redirect-using-spring-boot-interceptor I will try to google translate what is written in the above site.

Returns: true if the execution chain should proceed with the next interceptor or the handler itself. Else, DispatcherServlet assumes that this interceptor has already dealt with the response itself. True if the execution chain needs to proceed to the next interceptor or the handler itself. Otherwise, DispatcherServlet assumes that this interceptor has already processed the response itself.

I'm not sure what I'm saying in "If not ...", but I think the DispatcherServlet probably considers that a response has already been returned when it is returned fales with preHandle (otherwise the controller does not return false). Since the method of is started, does "if not" mean "if false"?). Anyway, if you don't want to invoke the controller method, write return false.

The Handler that passes to preHandle () is not always the HandlerMethod

-I am verifying whether the handler is a HandlerMethod in the Interceptor class, because the handler is not necessarily a HandlerMethod. -For example, if you try to send a request to an address that is not supposed to be mapped, ResourceHttpRequestHandler will be passed. ・ I referred to the following site. https://qiita.com/tukiyo320/items/d51ea698c848414b5874

reference: Set up interceptor with springboot Put processing before and after the controller method in Interceptor Pre-process the method of Controller with specific Annotation of Spring Boot WebMvcConfigurer Memorandum of Spring Boot 2.0 (Spring 5) Display error screen with Springboot I was addicted to using Spring Boot (about 1.2) [Updated from time to time] RedirectusingSpringbootinterceptor The method that comes to preHandle is not always HandlerMethod

Recommended Posts

Use Interceptor in Spring
How to use Lombok in Spring
Use DynamoDB query method in Spring Boot
Inject Logger in Spring
Use java.time in Jackson
Use Servlet filter in Spring Boot [Spring Boot 1.x, 2.x compatible]
Use OpenCV in Java
Microservices in Spring Cloud
Use MouseListener in Processing
Use images in Rails
Use PostgreSQL in Scala
[JAVA] [Spring] [MyBatis] Use IN () with SQL Builder
Get cookies in Spring
Use PreparedStatement in Java
How to use CommandLineRunner in Spring Batch of Spring Boot
[JAVA] [Spring] [MyBatis] Use GROUP BY in SQL Builder
How to use In-Memory Job repository in Spring Batch
Set context-param in Spring Boot
[spring] Let's use Spring Data JPA
Spring Boot 2 multi-project in Gradle
Use ruby variables in javascript.
Spring Boot + Springfox springfox-boot-starter 3.0.0 Use
Error in Spring database connection
Use Spring JDBC with Spring Boot
Major changes in Spring Boot 1.5
Use Redis Stream in Java
NoHttpResponseException in Spring Boot + WireMock
Use multiple checkboxes in Rails6!
Loop step in Spring Batch
How to call and use API in Java (Spring Boot)
Use thymeleaf3 with parent without specifying spring-boot-starter-parent in Spring Boot
Use @ControllerAdvice, @ExceptionHandler, HandlerExceptionResolver in Spring Boot to catch exceptions
Use voice recognition function in Unity
Use Basic Authentication with Spring Boot
Call Chain from Chain in Spring Integration
Spring Boot Hello World in Eclipse
Spring Boot application development in Eclipse
Use constructor with arguments in cucumber-picocontainer
How to use Spring Data JDBC
Java Spring environment in vs Code
I participated in JJUG CCC 2019 Spring
Let's use Twilio in Java! (Introduction)
[Rails] Use cookies in API mode
[Java] Do not use "+" in append!
Use composite keys in Java Map.
Implement reCAPTCHA v3 in Java / Spring
Use the Findbugs plugin in Eclipse
Store session information in database in Spring Session
How to set and use profile in annotation-based Configuration in Spring framework
How to use InjectorHolder in OpenAM
Implement REST API in Spring Boot
What is @Autowired in Spring boot?
Use DBUnit for Spring Boot test
Event processing is performed in Spring.
Implement Spring Boot application in Gradle
How to use ModelMapper (Spring boot)
How to use classes in Java?
Use completion in Eclipse on mac
Beginning with Spring Boot 0. Use Spring CLI
Address already in use workaround (Windows)
Thymeleaf usage notes in Spring Boot