728x90
스프링 인터셉터란?
- 스프링 인터셉터는 사실상 서블릿 필터와 동일한 기능을 제공한다.
- 다만 서블릿 필터가 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 스프링MVC 가 제공하는 기술로 적용되는 순서와 범위, 사용방법이 다르다.
- 스프링 인터셉터를 위해서는 HandlerInterceptor 인터페이스를 구현한다.
- 또한!! 스프링에서 제공하는 만큼 서블릿 필터보다 더 강력하고 좋다
스프링 인터셉터의 동작
HTTP 요청 → WAS → 필터 → 서블릿 → 스프링 인터셉터 → 컨트롤러
- 스프링 인터셉터는 디스패쳐 서블릿과 컨트롤러 사이에서 컨트롤러 직전에 호출된다 ⇒ 따라서 서블릿 필터 뒤에 실행된다
- 스프링 인터셉터는 결국 MVC 에서 제공하는 기능이기 때문에 결국 디스패쳐 서블릿 이후에 등작하게 된다. 이는 스프링 MVC 의 시작점이 디스패쳐 서블릿이라는 것을 생각해보면 오히려 이해가 된다.
- 스프링 인터셉터에도 URL 패턴을 적용할 수 있는데 서블릿 uRL 패턴과는 다르고, 매우 정밀하게 설정 가능하다.
HTTP 요청 → WAS → 필터 → 서블릿 → 인터셉터1 → 인터셉터2 → 컨트롤러
- 서블릿 필터와 마찬가지로 인터셉터도 여러개를 걸 수 있다
스프링 인터셉터의 흐름
정상 흐름
- PreHandle : 컨트롤러 호출전에 호출된다 → 핸들러 어댑터 호출전에 호출
- 이때 preHandle 의 응답값이 true 면 다음으로 진행하고, false 면 더는 진행하지 않는다. false 인 경우 나머지 인터셉터는 물론이고, 핸들러 어댑터도 호출되지 않는다. 즉 그림의 1번에서 끝!!
- PostHandle : 컨트롤러 호출 후에 호출된다. 더 정확히는 핸들러 어댑터 호출 후에 호출된다.
- afterCompletion : 뷰가 랜더링 된 이후에 호출된다.
예외 상황 발생
- PreHandle : 컨트롤러 호출전에 호출된다.
- postHandle : 컨트롤러에서 예외가 발생하는 경우 postHandle 은 호출되지 않는다.
- afterCompletion : 얘는 예외가 발생하던 말던 상관없이 무조건 호출된다.
스프링 인터셉터로 로그 남기기
LogInterceptor
- 서블릿 필터와 다른 점은 ServletRequest가 아닌 HttpServletRequest 가 바로 넘어온다는 점과 각 핸들러마다 handler 이라는 객체와 각 핸들러에 맞는 특징적인 객체 - modelAndView, Exception - 등이 함께 넘어온다는 점이다.
- 여기서 handler 는 MVC 의 handlerAdepter 를 의미하는 것으로 쉽게 이야기해서 스프링 컨트롤러를 의미한다. 따라서 이 핸들러 안에는 인터셉터 뒤에 오는 컨트롤러 메서드에 대한 모든 정보가 담긴다 ⇒ 이 정보들을 꺼내서 사용 가능하다
- postHandle 에는 ModelAndView 객체가 들어온다. 따라서 ModelAndView 를 꺼내서 사용 가능하다
package HJproject.Hellospring.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
public static final StringLOG_ID= "logId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String reqURI = request.getRequestURI();
String uuid = UUID.randomUUID().toString();
// preHandle 뒤에오는 인터셉터 핸들러에서 uuid 를 동일한 사용하기 위해서
request.setAttribute(LOG_ID, uuid);
// 컨트롤러에서 @RequestMapping 처럼 사용하는 경우 HandlerMethod 를 사용한다.
// 이쪽은 Controller 의 Handler 과 HandlerAdepter 와 관련있다
// 정적 리소스 : ResourceHttpRequestHandler
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler; // 호출할 컨트롤러 메서드의 모든 정보가 포함된다
}
log.info("REQUEST [{}] [{}] [{}] ", uuid, reqURI, handler);
return true;
}
// postHandle 는 ModelAndView 를 매개변수로 받는다
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandler [{}]", modelAndView);
}
// afterCompletion 은 Exception 를 매개변수로 받는다
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
String reqURI = request.getRequestURI();
String logId = (String)request.getAttribute(LOG_ID);
log.info("RESPONSE [{}] [{}] [{}]", logId, reqURI, handler);
if (ex != null) {
log.error("aferCompletion Error!!!!", ex);
}
}
}
SpringConfig 에 등록하기
- 등록은 서블릿 필터와는 다르게 Bean 등록이 아닌 WebMvcConfigurer 인터페이스를 오버라이딩해서 등록하는 방식이다.
@Configuration // 스프링 빈에 등록하기 위한 설정파일이라는 Annotation
public class SpringConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1)
.addPathPatterns("/**") // 패턴 적용 방식이 서블릿 패턴과는 상이하다
.excludePathPatterns("/css/**", "/*.ico", "/error"); // 예외 패턴에 대해서 작성 가능!
}
}
Spring 인터셉터 URL 패턴
- 스프링 인터셉터에서 제공하는 URL 패턴은 서블릿 필터에서 사용되던 URL 과는 완전히 다르다! 더욱 자세하고 세밀하게 설정 가능하다!
- 참고로 이 패턴은 정규식과도 은근 비슷하고, 특히나 스프링 시큐리티에서 사용되는 패턴과도 묘하게 일치한다...
패턴 | 적용 내용 |
? | 한 문자 일치 |
* | 경로 / 안에서 0 개 이사의 문자 일치 |
** | 경로 끝까지 0개 이상의 경로 / 일치 |
{SPRING} | 경로 / 와 일치하고 SPRING 이라는 변수로 캡처 |
{SPRING:[a-z]+} | 정규표현식 regexp [a-z] 와 일치하고 "SPRING" 이라는 경로 변수로 캡처 |
{*SPRING} | 경로가 끝날 때까지 0개 이상의 경로 / 와 일치하고 SPRING 이라는 변수로 캡처 |
PathPattern 공식문서에서 추가 내용을 찾아 볼 수 있다
/pages/t?st.html — matches /pages/test.html,
/pages/tXst.html but not /pages/ toast.html /resources/*.png — matches all .png files in the resources directory
/resources/** — matches all files underneath the /resources/ path, including / resources/image.png and /resources/css/spring.css
/resources/{*path} — matches all files underneath the /resources/ path and captures their relative path in a variable named "path"; /resources/image.png will match with "path" → "/image.png", and /resources/css/spring.css will match with "path" → "/css/spring.css"
/resources/{filename:\\w+}.dat will match /resources/spring.dat and assign the value "spring" to the filename variabl
로그찍기 코드 확인
- 로그는 서블릿 필터 뒤에 인터셉터의 로그가 남는다.
- 따라서 서블릿 필터 -> 스프링 인터셉터 라는 순서를 확인 가능하다.
- 로그인한 상태에서 찍히는 PostHandle 에서 ModelAndView 를 확인가능하다. 동시에 Handle 객체를 통해서 어떤 컨트롤러의 어떤 메서드를 사용하는지 매개변수가 무엇인지도 확인 가능하다!!
스프링 인터셉터 로그인 체크
LoginCheckInterceptor
package HJproject.Hellospring.interceptor;
import HJproject.Hellospring.Session.SessionConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
// 스프링 인터셉터는 서블릿 필터에서 사용하던 화이트 리스트를 따로 작성할 필요가 없다!
// ==> 단, SpringConfig 에 추후 Interceptor 을 등록할때 특정 url 을 등록하면 된다.
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String reqURI = request.getRequestURI();
log.info("인증 체크 인터셉터 실행!! {}", reqURI);
HttpSession session = request.getSession();
if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
log.info("미인증 사용자 요청");
// 로그인으로 redirect
response.sendRedirect("/login?redirectURL="+reqURI);
return false;
}
return true;
}
}
SpringConfig
@Configuration // 스프링 빈에 등록하기 위한 설정파일이라는 Annotation
public class SpringConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1) // 인터셉터 순서
.addPathPatterns("/**") // 패턴 적용 url
.excludePathPatterns("/css/**", "/*.ico", "/error"); // 패턴 예외 url
registry.addInterceptor(new LoginCheckInterceptor())
.order(2) // 인터셉터 순서
.addPathPatterns("/**") // 패턴을 적용하고자 하는 url
// 패턴 예외 url
.excludePathPatterns("/**/js/*.js", "/index", "/", "/home", "/members/newregisters", "/login", "/logout","/css/**", "/*.ico", "/error");
}
// Servlet Filter 를 사용하기 위한 Bean
//@Bean
public FilterRegistrationBean logFilter(){
FilterRegistrationBean<Filter> filterBean = new FilterRegistrationBean<Filter>();
filterBean.setFilter(new LogFilter());
// 필터 순서 => 필터 체인 시 사용되는 순서
filterBean.setOrder(1);
// 필수 적용 URL => 필터 적용 시 사용되는 url : /* 라면 모든 url 에 적용됨
filterBean.addUrlPatterns("/*");
return filterBean;
}
}
로그인 체크 코드 확인
- 로그인 없이 members/member_list 에 접근을 시도하자 인터셉터가 발동하여 접근을 막은 모습
- admin 으로 로그인 인터셉터에 걸리지 않고 접근 가능하다
'Java - Spring &&n SpringBoot' 카테고리의 다른 글
웹 네트워크 기본 공부 2) HTTP 알아보기 (0) | 2022.08.17 |
---|---|
Spring - ArgumentResolver (feat.커스텀 어노테이션, 세션) (0) | 2022.08.17 |
Spring - 서블릿 필터 다루기(2) : 로그인 여부 체크, 로그인 여부에 따른 페이지 접근 (0) | 2022.08.12 |
Spring - 서블릿 필터와 스프링 인터셉터(1) 서블릿필터, 서블릿 필터를 사용한 로그 찍기 (0) | 2022.08.11 |
Spring Session 다루기 : 세션 정보, 세션 타임 아웃 설정 (0) | 2022.08.11 |
댓글