블로그 이름 뭐하지
[Spring] 필터(Filter) 본문
좌측 사진은 Spring 프레임워크에 대한 라이프 사이클 과정으로
일반적으로 요청은 DispatcherServlet에 의해 Controller에 매핑된다.
하지만 Filter는 요청이 DispatcherServlet에 의해 다뤄지기 전에 동작한다.
그리고 모든 작업이 끝난 이후 Client에게 응답하기 전 마지막으로 동작한다.
즉 Filter는 Client의 요청, 응답에 대해 최초, 최종 단계로 거치는 구간이다.
우측 사진은 Filter Chain을 나타내는 것으로
Filter가 하나만 존재하지 않고, 여러 개가 Chain 형식으로 묶여 처리되는 것을 보여준다.
즉, 여러 작업을 Filter에서 진행할 수 있다는 뜻이다.
우리는 이런 Filter의 특징을 통해 요청과 응답의 정보를 변경하거나 부가적인 기능을 추가할 수 있다.
주로 범용적으로 처리하는 작업에 사용된다. (ex. 로깅, 보안처리, 인증인가 로직 등)
Filter 등록
Filter를 사용하기 위해서는 Filter를 등록하는 과정이 필요하다.
Servlet 컨테이너에서 관리
서블릿 컨테이너에서 관리하게 하는 등록 방법이다.
최근에는 잘 사용하지 않으며, Spring 환경이 아닌 Servlet 환경일 경우에만 사용을 권장한다.
1) web.xml을 이용해 등록
<!-- web.xml -->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 필터 등록 -->
<filter> // : 필터 등록
<filter-name>LoggingFilter</filter-name> // : 필터 이름(고유)
<filter-class>com.example.LoggingFilter</filter-class> // : 필터 패키지 전체 경로
</filter>
<!-- 필터 매핑 -->
<filter-mapping> // : 매핑 정보 입력
<filter-name>LoggingFilter</filter-name> // : 필터 이름
<url-pattern>/*</url-pattern> //: 필터와 Url을 매핑할 경로
</filter-mapping>
</web-app>
2) @WebFilter 어노테이션으로 등록
@WebFilter("/*") // Url 패턴을 지정할 수 있다.
// 해당 어노테이션은 Servlet3.0 이후 부터 사용이 가능하다
// 사용시에는 @ServletComponentScan을 SpringBootApplication에 적용해야한다.
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 필터 초기화 코드
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 필터 동작 코드 (전처리)
System.out.println("Request received at " + System.currentTimeMillis());
// 다음 필터로 체인 전달 또는 실제 서블릿 호출
chain.doFilter(request, response);
// 필터 동작 코드 (후처리)
System.out.println("Response sent at " + System.currentTimeMillis());
}
@Override
public void destroy() {
// 필터 소멸 코드
}
}
Spring IoC 컨테이너에서 관리
IoC 컨테이너에서 Bean으로 관리하게 하는 등록 방법이다.
DI의 문제로 요즘은 이 방법을 많이 채택한다.
1) @Component 어노테이션으로 등록
@Component // 가장 간단한 방법이다. Filter 위에 어노테이션을 입력한다
@Order(1)
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 전처리
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String url = httpServletRequest.getRequestURI();
log.info(url);
chain.doFilter(request, response); // 다음 Filter 로 이동
// 후처리
log.info("비즈니스 로직 완료");
}
}
2) @Configuration, @Bean으로 수동 등록
@Configuration //class에는 configuration
public class FilterConfiguration {
@Bean //메서드에는 bean
public SomeFilter someFilter() {
return new SomeFilter();
}
}
Filter 생성
Filter는 Sevlet의 Filter 인터페이스를 구현해서 만든다.
Filter 인터페이스는 3가지 메서드를 가지고 있다.
- init() : 필터가 처음 생성될 때 호출되어 초기 설정을 위한 코드가 들어간다.
보통 필터 인스턴스를 초기화 하거나 필요한 리소스를 준비할 때 사용한다.
(ex. 데이터 베이스 연결 초기화, 외부 설정 파일 로드) - doFilter() : 실제 필터링 작업을 수행한다.
모든 요청에 로직을 적용하고, 다음 필터나 최종 서블릿으로 요청을 전달한다.
복잡하지 않은 필터에서는 해당 메서드만 작성하는 경우가 많다. - destory() : 필터가 소멸될 때 호출되어 필터에서 사용한 리소스를 해제할 때 사용한다.
(ex. 데이터 베이스 연결 닫기, 파일 핸들 닫기)
Filter 예시
아래의 코드는 로깅과 인증처리에 관한 필터들이다.
package com.sparta.springauth.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Slf4j(topic = "LoggingFilter") // 로깅
@Component // Bean으로 등록
@Order(1) // 필터의 순서를 지정함. 숫자가 낮을 수록 먼저 시행된다
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 전처리
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String url = httpServletRequest.getRequestURI();
log.info(url);
chain.doFilter(request, response); // 다음 Filter 로 이동
// 후처리
log.info("비즈니스 로직 완료");
}
}
package com.sparta.springauth.filter;
import com.sparta.springauth.entity.User;
import com.sparta.springauth.jwt.JwtUtil;
import com.sparta.springauth.repository.UserRepository;
import io.jsonwebtoken.Claims;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.IOException;
@Slf4j(topic = "AuthFilter")
@Component
@Order(2)
public class AuthFilter implements Filter {
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
public AuthFilter(UserRepository userRepository, JwtUtil jwtUtil) {
this.userRepository = userRepository;
this.jwtUtil = jwtUtil;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String url = httpServletRequest.getRequestURI();
if (StringUtils.hasText(url) &&
(url.startsWith("/api/user") || url.startsWith("/css") || url.startsWith("/js"))
) {
// 위에 지정된 url들은 filter의 로직(인증처리)을 거치지 않도록 한다.
chain.doFilter(request, response); // 다음 Filter 로 이동
} else {
// 나머지 API 요청은 인증 처리 진행
// 토큰 확인
String tokenValue = jwtUtil.getTokenFromRequest(httpServletRequest);
if (StringUtils.hasText(tokenValue)) { // 토큰이 존재하면 검증 시작
// JWT 토큰 substring
String token = jwtUtil.substringToken(tokenValue);
// 토큰 검증
if (!jwtUtil.validateToken(token)) {
throw new IllegalArgumentException("Token Error");
}
// 토큰에서 사용자 정보 가져오기
Claims info = jwtUtil.getUserInfoFromToken(token);
User user = userRepository.findByUsername(info.getSubject()).orElseThrow(() ->
new NullPointerException("Not Found User")
);
request.setAttribute("user", user);
chain.doFilter(request, response); // 다음 Filter 로 이동
} else {
throw new IllegalArgumentException("Not Found Token");
}
}
}
}
참고한 링크
spring - 스프링에서의 필터 개념 및 예제
블로그에서 사용한 소스코드는 https://github.com/97e57e/BLOG 에서 보실 수 있습니다. Filter 란? 사실 필터는 스프링의 독자적인 기능이 아닌 자바 서블릿에서 제공하는 기능입니다. 스프링 프레임워크
gardeny.tistory.com
[Spring] Filter 이해하기
이전 글에서 Filter는 요청에 의해서 실행될 Servlet 실행 전/후로 특별한 처리가 필요한 경우 실행되는 클래스라고 했다. 즉, 클라이언트의 HTTP 요청이 Servlet에 도달하기 전이나 후에 요청/응답 데
velog.io
'Spring' 카테고리의 다른 글
[Spring] Validation (0) | 2024.11.14 |
---|---|
[Spring] Spring Security (0) | 2024.11.14 |
[Spring] 쿠키와 세션, JWT (0) | 2024.11.13 |
[Spring] Bean (0) | 2024.11.13 |
[Spring] JPA (0) | 2024.11.13 |