본문 바로가기
BackEnd/Spring

[Spring] MVC HandlerInterceptor / Interceptor를 사용하여 HTTP 요청을 가로채는 방법

by 성은2 2023. 6. 19.

Spring Handler Interceptor

Simply put, a Spring interceptor is a class that either extends the HandlerInterceptorAdapter class or implements the HandlerInterceptor interface.

public class LoggerInterceptor implements HandlerInterceptor {

...

}

 

Spring Interceptor는 

HandlerInterceptor 인터페이스를 구현 하거나,

HandlerInterceptorAdapter 클래스를 상속 하는 클래스다.

 

HandlerInterceptor 인터페이스를 구현

HandlerInterceptor 에는 3가지 메인 메서드가 있으며, HandlerInterceptor 인터페이스를 구현하기 위해서는 이 세가지 메서드를 구현해야 한다.

  • prehandle() – called before the execution of the actual handler
  • postHandle() – called after the handler is executed
  • afterCompletion() – called after the complete request is finished and the view is generated

 

 

 

1.  preHandle() 

이름에서 알 수 있듯이 요청을 처리하기 전에 preHandle()을 호출한다.

리턴값은 boolean으로, true / false 값을 리턴하도록 되어 있다.

"각 메서드의 반환값이 true이면 핸들러의 다음 체인이 실행되지만 false이면 중단하고 남은 인터셉터와  컨트롤러가 실행되지 않습니다."

기본적으로 이 메서드는 true를 반환하지만, 처리에 따라 false를 반환해서 Spring에게 로직 수행을 중단하라고 말할 수 있다. 사용의 예시로는 request parameter에 대한 로그 정보를 남길 수 있는데, simple Log4J logger를 사용하여 커스텀화 해서 사용하는 예시가 있다.

public class TestInterceptor implements HandlerInterceptor {

	// controller로 보내기 전에 처리하는 인터셉터
	// 반환이 false라면 controller로 요청을 안함
	// 매개변수 Object는 핸들러 정보를 의미한다. ( RequestMapping , DefaultServletHandler ) 

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

        // your code
        System.out.println("컨트롤러의 메서드가 실행되기 전에 실행됨");
        log.info("[preHandle][" + request + "]" + "[" + request.getMethod() + "]" + request.getRequestURI() + getParameters(request));

        // 로그인 여부 확인 용도
        HttpSession session = request.getSession();
        String requestUrl = request.getRequestURI().toString();


        if (requestUrl.contains("/resources/")) { // 리소스 경로 제외
            } else if (requestUrl.contains("/api/")) {

                String mbrNo = (String) session.getAttribute("sess_mbrNo");

                if (mbrNo != null) {
                    String ori_mbrNo = session.getAttribute("_hdn_mbrNo_").toString();
                    if (!mbrNo.equals(ori_mbrNo)) {
                        System.out.println("로그인 세션 회원 번호 불일치 : mbrNo [" + mbrNo + "] ori_mbrNo >" + ori_mbrNo);
                        response.sendRedirect("/common/logout");
                        return false;
                    }
                }
            } else if (requestUrl.contains("/common/test")) {
                // 특정 url 처리
                // your code

            } else {
                // 로그인 체크하여 로그인 페이지로 이동 처리
                String webId = (String) session.getAttribute("sess_webId");
                if (webId == null) {
                    response.sendRedirect("/signin/login?call_back_url=" + requestUrl);
                    return false;
                }
            }

        return true;

	}
}

 

2. postHandle() 

인터셉터는 요청을 처리한 후 DispatcherServlet이 View를 생성(렌더링)하기 에 이 메서드를 호출합니다.

@Override

public void postHandle( HttpServletRequest request, HttpServletResponse response
, Object handler, ModelAndView modelAndView) throws Exception {

	// your code

	System.out.println("컨트롤러의 메서드가 실행된 후에 실행됨 ");

}

 

 

3. afterCompletion() 

이 방법을 사용하여 View가 렌더링된 요청 및 응답 데이터를 얻을 수 있습니다:

 

 
@Override

public void afterCompletion( HttpServletRequest request, HttpServletResponse response
, Object handler, Exception ex) {

    // your code

    if (ex != null){
    	ex.printStackTrace();
    } 

    log.info("[afterCompletion][" + request + "][exception: " + ex + "]");

}

 

 

이제 모든 조각들을 합쳤으니 사용자 지정 interceptor를 추가해서 사용 가능 하다.
그러기 위해서는 addIntercepters() 메서드를 Override해야 한다:

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

 

* 뷰 페이지에서는 로그인 버튼을 눌렀을 때 요청이 /user/login으로 요청이 갈 수 있도록 form을 다음과 같이 작성합니다.

<form method="post" action="${pageContext.servletContext.contextPath }/user/login">

 

 

* XML Spring 구성 파일을 편집하여 동일한 구성을 수행할 수 있다:

<mvc:interceptors>
    <bean id="loggerInterceptor" class="com.baeldung.web.interceptor.LoggerInterceptor"/>
</mvc:interceptors>

= > 이 configuration을 활성화 하면, loggerInterceptor가 활성화 되고, 어플리케이션의 모든 요청이 올바르게 기록된다.

 

* 혹은, 특정 경로로 요청이 왔을때 사용하고 싶은 interceptor를 매칭시킬 수도 있다.

<mvc:interceptor>
	<!-- /user/login 경로인 경우 AuthLoginInterceptor 객체의 preHandle() 메서드 실행 -->
        <mvc:mapping path="/user/login" /> 
        <bean class="com.victolee.security.AuthLoginInterceptor" />
</mvc:interceptor>

 

 

 

 

 

HandlerInterceptorAdapter 클래스를 상속 받아 구현

public class MyInterceptor extends HandlerInterceptorAdapter {
	@Override
	public boolean preHandle(
			HttpServletRequest request, HttpServletResponse response,
			Object obj) throws Exception {
		
		System.out.println("MyInterCeptor - preHandle");
		return true;
	}
}

HandlerInterceptorAdapter 클래스를 상속 받으면, postHandle() , afterCompletion() 메서드가 구현되어 있으므로 필요한 메서드만 오버라이딩 하면 됩니다.

 

Spring 인터셉터가 여러 개 구성된 경우 preHandle() 메서드는 구성 순서대로 실행되고 postHandle() 및 afterCompletion() 메서드는 역순으로 호출된다.

 

 

 

https://www.baeldung.com/spring-mvc-handlerinterceptor

https://victorydntmd.tistory.com/176

 

[Spring] Interceptor (1) - 개념 및 예제

인터셉터 ( Interceptor )Interceptor란 컨트롤러에 들어오는 요청 HttpRequest와 컨트롤러가 응답하는 HttpResponse를 가로채는 역할을 합니다.인터셉터는 관리자만 접근할 수 있는 관리자 페이지에 접근하

victorydntmd.tistory.com