Spring Interceptor
스프링 인터셉터는 스프링 MVC가 제공하는 웹과 관련된 공통 관심사항을 효과적으로 해결할 수 있는 기술이다.
예를 들어 로그인되지 않은 사용자가 로그인해야지만 접근할 수 있는 페이지에 접근하려 할 때,
세션을 확인하여 세션에 로그인된 사용자의 유무에 따라 접근 허용 여부를 설정해 줄 수 있다.
1) 스프링 인터셉터 흐름
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
- 서블릿 필터는 서블릿이 호출되기 이전에 호출된다.
- 스프링 인터셉터는 컨트롤러 호출 직전에 호출된다.
- URL패턴을 서블릿 필터에 비해 정밀하게 설정할 수 있다.
2) 스프링 인터셉터 제한
1. 접근 허용 시
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
2. 접근 거절 시
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터(접근 거부, 컨트롤러 호출 x) -> 종료
3) 스프링 인터셉터 체인
스프링 인터셉터는 체인으로 구성되고, 순서를 자유롭게 변경할 수 있다.
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터 1 -> 인터셉터 2... -> 컨트롤러
interface HandlerInterceptor
preHandle
- 컨트롤러 호출 전
- return true : 다음 인터셉터 또는 컨트롤러가 호출된다.
- return false : 다음이 호출되지 않고 종료된다.
postHandle
- 컨트롤러에서 예외가 발생하면 호출되지 않는다.
afterCompletion
- 요청 완료 이후 항상 호출된다.
- 예외 발생 시 예외 [ Exception ex ]를 파라미터로 받아서 예외 정보를 로그로 출력할 수 있다.
3개의 메서드 중 원하는 것만 구현해서 사용하면 된다.
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler)throws Exception { return true;}
default void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
@Nullable ModelAndView modelAndView) throws Exception { }
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
@Nullable Exception ex) throws Exception { }
Configuration
HandlerInterceptor를 implements 하여 메서드를 구현한 클래스를 만든 뒤,
WebConfig를 만들어서 addInterceptors 메서드를 구현해야 한다.
addInterceptors 메서드 내에 registry.addInterceptor를 통해 인터셉터를 추가하면 된다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(/*인터셉터 생성*/)
.order(1) //첫번째로 실행 될 인터셉터로 등록
.addPathPatterns(/*URL 경로*/)
.excludePathPatterns(/*제외할 URL 경로*/);
}
}
- . order(int n) : n번째로 실행할 인터셉터로 등록
- . addPathPatterns() : ( ) 하위에 전부 적용
- . excludePathPatterns 등록되었지만, 그중 제외할 URL 지정
예제
예제 1) 로그 인터셉터
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
public static String LOG_ID = "logId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String requestURI = request.getRequestURI();
String uuid = UUID.randomUUID().toString(); // uuid 생성
request.setAttribute(LOG_ID,uuid); //afterCompletion에서 사용하기위해 request에 넣는다.
//@RequestMapping을 사용하는 경우 : HandlerMethod가 사용된다.
//정적 리소스를 사용하는 경우 : ResourceHttpRequestHandler 가 사용된다.
if(handler instanceof HandlerMethod){
HandlerMethod hm = (HandlerMethod) handler;
//HandlerMethod의 여러가지 메소드를 사용하여 호출할 컨트롤러 메서드의 정보를 얻을 수 있다.
}
log.info("----------new REQUEST------------");
log.info("REQUEST [{}][{}] | uuid : [{}]", requestURI, handler, uuid);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandler [{}]", modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
String requestURI = request.getRequestURI();
//위의 preHandle에서 생성한 uuid를 request에 넣어뒀던것을 가져온다.
String uuid = (String) request.getAttribute(LOG_ID);
log.info("RESPONSE [{}][{}] | uuid : [{}]", requestURI, handler, uuid);
//오류발생시 오류 로그도 찍어주자.
if(ex!=null){
log.error("afterCompletion error!!", ex);
}
}
}
예제 2) 로그인 체크 인터셉터
preHandle만 필요해서 필요한 것만 구현했다.
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("로그인 체크 인터셉터 실행 [{}]", requestURI);
HttpSession session = request.getSession();//request에서 session을 가져온다
// 세션이 비어있거나, 세션에서 "loginId"가 비어있을때 접근 거부를 위한 로직
if(session == null || session.getAttribute("loginId") == null){
log.info("미인증 사용자 요청");
// [redirectURL=(requestURI)&msg=true] 라는 쿼리를 달아서 로그인 화면으로 redirect한다.
response.sendRedirect("/login?redirectURL="+requestURI+"&msg=true");
//false 반환 시 다음 인터셉터가 호출되지 않는다.
return false;
}
return true;
}
}
WebConfig 등록
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.order(1) //첫번째로 실행 될 인터셉터로 등록
.addPathPatterns("/**") // "/"하위에 전부 적용
.excludePathPatterns("/css/**","/*.ico","/error");
registry.addInterceptor(new LoginCheckInterceptor())
.order(2) //두번째로 실행 될 인터셉터로 등록
.addPathPatterns("/**")
.excludePathPatterns("/","/member/add","/board","/board/post/*","/login",
"/logout","/css/**","/*.ico","/error");
}
}
"/**"
: / 하위의 모든 경로"/board"
: 정확히 '/board'만 해당/css/**
: /css로 시작하는 모든 경로/*. ico
끝나는 경로 제외/board/post/*
: /board/post/(숫자)인 경로
(참고) 김영한 님 인프런 Spring MVC-2
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-
반응형
'JAVA & Spring > Spring' 카테고리의 다른 글
[Spring] 세션, 쿠키, @SessionAttribute 로그인 확인 (0) | 2023.04.21 |
---|---|
[Spring Boot] PRG 패턴 ( Post - Redirect - Get ) (1) | 2023.04.21 |
[Spring Boot] @PathVariable과 @RequestParam - 파라미터 받기 (0) | 2023.04.21 |
[Spring] Bean Validation (0) | 2023.04.21 |
[Spring] 메시지, 국제화 (0) | 2023.04.21 |