본 글에서는 모든 Rest API에 특정작업을 Rest API 종류와 무관하게 모두 적용할 수 있는 Spring Filter에 대해 알아보겠습니다. Controller에서 몇개의 메서드가 있던, Http Method 방식이 Get이건 Post이건 무관하게 Filter를 구축해놓으면 모든 Rest API에 대해 공통작업을 할 수 있는데, 주로 인증/인가 혹은 로깅에 사용할 수 있습니다.
핵심 코드는 chain.doFilter(request, response)이며,doFilter 이전의 코드는 Controller 실행 전, doFilter 이후의 코드는 Controller 실행 후 실행된다는 특징이 있습니다. doFilter를 호출해야 다음번 필터로 넘어가거나, 다음번 필터가 없으면 디스패처 서블릿에게 넘어가기 때문에 Filter내에서는 doFilter가 필수적으로 호출되어야 합니다.
코드를 통해 이해해보겠습니다.
- FilterConfig
TimeCheckFilter와 AuthorizationFilter를 선언하였으며, 각각 실행 시간 및 로깅을 담당하는 필터를 만드려 합니다. setOrder를 통해 필터의 실행 순서를 정할수 있습니다. (Client -> 1번 필터 -> 2번 필터 -> Controller -> 2번 필터 -> 1번 필터) 또한 추가적으로 .addUrlPatterns를 통해, 어떤 URL 요청에 필터를 적용할지를 설정할 수 있습니다. "/api/*"으로 설정하면, api/test 혹은 api/abc 이런식으로 들어오는 모든 Http 요청에 대해 필터를 적용할 수 있습니다.
@Slf4j
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<Filter> timeCheckFilter() {
FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
filterFilterRegistrationBean.setFilter(new TimeCheckFilter());
filterFilterRegistrationBean.setOrder(1);
filterFilterRegistrationBean.addUrlPatterns("/api/*");
return filterFilterRegistrationBean;
}
@Bean
public FilterRegistrationBean<Filter> authorizationFilter() {
FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
filterFilterRegistrationBean.setFilter(new AuthorizationFilter());
filterFilterRegistrationBean.setOrder(2);
filterFilterRegistrationBean.addUrlPatterns("/api/*");
return filterFilterRegistrationBean;
}
}
- TimeCheckFilter
Filter 클래스를 implements 해야하며, public void doFilter를 Override해야 합니다. filterChain.doFilter 코드가 핵심코드이고 해당 코드 전은 Controller전에 수행, 해당 코드 후는 Controller 후에 수행됩니다.
@Slf4j
public class TimeCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
String method = req.getMethod();
StringBuffer requestURL = req.getRequestURL();
long startTime = System.currentTimeMillis();
try {
log.info("TimeCheckFilter Method : {}, url : {},",method,requestURL);
//핵심 코드 doFilter
filterChain.doFilter(servletRequest,servletResponse);
}
catch (Exception e){
throw e;
}
finally {
long endTime = System.currentTimeMillis();
long gap = endTime - startTime;
log.info("FilterElaspedTime : {} ms",gap);
}
}
}
- AuthorizationFilter
마찬가지로 Filter 클래스를 implements 해야하며, public void doFilter를 Override해야 합니다. apikey라는 헤더를 통해 인증/인가를 구현해보았습니다. 아래 코드상으로는 "testkey123"으로 하드코딩 되어있지만, 실무에서는 DB에서 불러오는 식으로 구현해야합니다.
@Slf4j
public class AuthorizationFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
String apiKey = req.getHeader("apikey");
try{
if(!"testkey123".equals(apiKey)) {
throw new RuntimeException("filter invalid api key");
}
//핵심 코드 doFilter
filterChain.doFilter(servletRequest,servletResponse);
}
catch (Exception e){
throw e;
}
finally {
log.info("AuthorizationFilter is applied");
}
}
}
필터가 정상적으로 적용되는지 확인하기 위해서, 아래와 같이 breakpoint를 설정하고 Run이 아닌 Debug를 통해 서버를 실행시켜야 합니다.
Swagger을 통해 아무 restapi를 요청해보면(http://localhost:8080/api/RequestBody/test2), 아래와 같이 Swagger에서는 Loading이 뜨고 응답받지 않은 상태로 멈춰있고, Intellij Debugger를 통해 천천히 분석할 수 있습니다. Step Over(F8) 버튼을 누르면서 다음 필터까지 넘어가는 부분도 확인 가능합니다.
'spring' 카테고리의 다른 글
spring RequestBodyAdviceAdapter를 통한 apiKey 인증인가 적용 (0) | 2023.06.04 |
---|---|
spring Filter에 @RequestBody 불러오기 / apiKey 인증인가 적용 (0) | 2023.06.04 |
spring 파라미터와 Request정보가 같이 필요할 때 처리방법 (1) | 2023.05.20 |
spring Interceptor (0) | 2023.05.20 |
spring Bean Validation 적용(@NotNull, @NotEmpty, @NotBlank, @Max, @Min) (0) | 2023.05.19 |
spring RestAPI 어노테이션 정리 (0) | 2023.05.14 |
spring Rest API Request(RestTemplate, Feign 비교) (0) | 2023.05.12 |
spring Bean 개념정리 2편(@Configuration, @Bean, @Qualifier, @Primary) (1) | 2023.05.10 |
댓글