[Java & Spring Filter] Filter
- Spring에서 필터(Filter) 관련 정리
- 필터 종류 및 CORS, XSS 예시
Spring에서 필터(Filter) 관련 정리
Spring에서 필터(Filter)는 클라이언트 요청을 가로채서 사전/사후 처리하는 기능을 수행합니다.
Spring Boot에서는 서블릿 컨텍스트와 Spring 컨텍스트가 따로 존재하며, 필터는 원래 서블릿 컨텍스트(ServletContext)에서 실행됩니다.
그러나 Spring Boot에서는 FilterRegistrationBean
을 사용하여 Spring 컨텍스트의 빈을 필터에서 사용할 수 있습니다.
1. 필터(Filter)의 개념
✅ 필터란?
- 요청(Request)과 응답(Response)을 가로채서 전처리/후처리하는 역할.
- 서블릿(Servlet) 기반에서 동작하며, Spring MVC의
DispatcherServlet
이전에 실행됨. - 주로 보안(인증, CORS), 로깅, 데이터 변환, 응답 압축 등에 사용됨.
✅ 필터와 인터셉터의 차이점
비교 항목 | 필터 (Filter) | 인터셉터 (Interceptor) |
---|---|---|
실행 위치 | DispatcherServlet 이전 (서블릿 레벨) | DispatcherServlet 이후 (Spring MVC 레벨) |
적용 범위 | 모든 요청 (정적 리소스 포함) | Spring MVC 컨트롤러 요청만 적용 |
사용 목적 | CORS, 로깅, 보안, 인코딩 설정 | 인증, 권한 체크, 로깅, 트랜잭션 관리 |
등록 방식 | FilterRegistrationBean 또는 @Component | WebMvcConfigurer 에서 addInterceptors() |
Spring Security 영향 | Security Filter Chain 앞에서 동작 | Security 필터 이후 동작 가능 |
2. 필터(Filter) 구현 및 등록 방법
Spring Boot에서는 3가지 방법으로 필터를 등록할 수 있습니다.
✅ 방법 1: FilterRegistrationBean
을 사용한 수동 등록 (추천)
- Spring 컨텍스트에서 필터를 관리하면서, 실행 순서를 제어할 수 있음.
- 필터에서
@Autowired
빈을 사용할 수 있음.
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
private final SomeService someService;
public FilterConfig(SomeService someService) {
this.someService = someService;
}
@Bean
public FilterRegistrationBean<CustomFilter> loggingFilter() {
FilterRegistrationBean<CustomFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new CustomFilter(someService)); // ✅ 빈 주입 가능
registrationBean.addUrlPatterns("/*"); // 모든 요청에 필터 적용
registrationBean.setOrder(1); // 필터 실행 순서 지정
return registrationBean;
}
}
필터 클래스 구현
import javax.servlet.*;
import java.io.IOException;
public class CustomFilter implements Filter {
private final SomeService someService;
public CustomFilter(SomeService someService) {
this.someService = someService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
someService.doSomething(); // ✅ Spring 빈 사용 가능
chain.doFilter(request, response);
}
}
📌 Spring 컨텍스트에서 필터를 관리하기 때문에 @Autowired
빈을 주입할 수 있음!
📌 필터 실행 순서(setOrder
)를 지정할 수 있어 다른 필터보다 먼저/나중에 실행 가능.
✅ 방법 2: @Component
를 사용한 자동 등록
- Spring이 자동으로 필터를 감지하여 등록.
- Spring 빈을 사용할 수 있지만, 실행 순서 제어가 어려움.
import org.springframework.stereotype.Component;
import javax.servlet.*;
import java.io.IOException;
@Component // ✅ 필터를 Spring 빈으로 등록
public class CustomFilter implements Filter {
private final SomeService someService;
public CustomFilter(SomeService someService) {
this.someService = someService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
someService.doSomething();
chain.doFilter(request, response);
}
}
📌 단점: @Component
로 등록하면 실행 순서를 setOrder
로 지정할 수 없음.
✅ 방법 3: @WebFilter
를 사용한 서블릿 기반 등록
- Spring과 무관하게 서블릿 컨텍스트에서 필터를 등록.
- Spring의 빈을 직접 주입할 수 없음.
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/*") // ✅ 서블릿 컨텍스트에서 필터 등록
public class CustomFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("Request received");
chain.doFilter(request, response);
}
}
📌 단점: Spring 빈을 사용할 수 없음 → SomeService someService;
를 주입하면 NullPointerException
발생.
3. 필터에서 빈 사용 가능 여부
방법 | 빈(@Autowired) 사용 가능 여부 | 실행 순서 지정 가능 여부 |
---|---|---|
FilterRegistrationBean 사용 (추천) | ✅ 가능 | ✅ 가능 |
@Component 사용 | ✅ 가능 | ❌ 불가능 |
@WebFilter 사용 (서블릿 방식) | ❌ 불가능 | ❌ 불가능 |
✅ 필터에서 빈을 사용하려면 FilterRegistrationBean
을 사용해야 함!
🚨 @WebFilter
는 서블릿 컨텍스트에서 관리되므로 빈을 주입할 수 없음.
4. 필터 실행 순서
필터는 실행 순서를 설정할 수 있습니다.
✅ 필터 실행 순서 지정 (setOrder
)
@Bean
public FilterRegistrationBean<FirstFilter> firstFilter() {
FilterRegistrationBean<FirstFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new FirstFilter());
registrationBean.setOrder(1); // 가장 먼저 실행
return registrationBean;
}
@Bean
public FilterRegistrationBean<SecondFilter> secondFilter() {
FilterRegistrationBean<SecondFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new SecondFilter());
registrationBean.setOrder(2); // 두 번째 실행
return registrationBean;
}
📌 낮은 숫자의 필터가 먼저 실행됨!
📌 인터셉터보다 필터가 먼저 실행됨.
5. 필터 실행 흐름
[클라이언트 요청]
→ 필터(Filter) 실행 (서블릿 컨텍스트)
→ DispatcherServlet 실행 (Spring 컨텍스트)
→ 인터셉터(Interceptor) 실행
→ 컨트롤러(Controller) 실행
→ 인터셉터(Interceptor) 실행 (afterCompletion)
→ DispatcherServlet 반환
→ 필터(Filter) 실행
→ [클라이언트 응답]
✅ 필터는 서블릿 컨텍스트에서 실행되므로, DispatcherServlet
보다 먼저 실행됨.
✅ 인터셉터는 DispatcherServlet
이후에 실행됨.
6. 결론
🚀 필터는 Spring IoC 컨텍스트와 무관하지만, FilterRegistrationBean
을 사용하면 Spring 빈을 사용할 수 있음.
🔥 필터를 사용하려면 FilterRegistrationBean
을 추천! (@Component
도 가능하지만 실행 순서 조정이 어려움.)
💡 서블릿 컨텍스트에서 실행되는 필터와 Spring 컨텍스트의 차이를 이해하면 필터를 더 효과적으로 활용할 수 있음! 🚀
필터 종류 및 CORS, XSS 예시
1. Spring에서 제공하는 주요 필터 종류
Spring은 javax.servlet.Filter
뿐만 아니라, 몇 가지 유용한 필터 클래스를 제공합니다.
✅ 1.1. OncePerRequestFilter
(한 요청당 한 번만 실행)
- 한 요청에 대해 필터가 여러 번 실행되지 않도록 보장하는 Spring 제공 필터.
- 기본 필터(
Filter
인터페이스)를 사용하면 같은 요청이 여러 번 필터링될 수도 있음. OncePerRequestFilter
를 확장하면 동일한 요청에 대해 한 번만 실행되도록 보장됨.
📌 예제: 요청 로깅 필터
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
System.out.println("LoggingFilter: " + request.getRequestURI());
filterChain.doFilter(request, response);
}
}
✅ 1.2. CharacterEncodingFilter
(문자 인코딩 설정)
- 요청과 응답의 문자 인코딩(UTF-8 등)을 설정하는 필터.
- Spring Boot에서는 자동으로 등록되지만, 설정을 커스터마이징할 수도 있음.
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public CharacterEncodingFilter encodingFilter() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
return filter;
}
}
📌 요청과 응답의 인코딩을 UTF-8로 설정하여 한글 깨짐 방지.
✅ 1.3. CorsFilter
(CORS 처리)
- CORS(Cross-Origin Resource Sharing) 정책을 적용하는 필터.
- 외부 도메인에서 API를 호출할 때 브라우저에서 차단되는 문제를 해결함.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*"); // 모든 도메인 허용
config.addAllowedMethod("*"); // 모든 HTTP 메서드 허용
config.addAllowedHeader("*"); // 모든 헤더 허용
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
📌 모든 도메인에서 API를 호출할 수 있도록 CORS 설정 적용.
✅ 1.4. HiddenHttpMethodFilter
(RESTful API 지원)
- HTML
<form>
태그는GET
과POST
만 지원하지만, PUT, DELETE 등의 메서드를 사용할 수 있도록 변환해줌. PUT
,DELETE
요청을POST
요청으로 보내면서_method
파라미터를 포함하면 변환됨.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;
@Configuration
public class FilterConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter();
}
}
📌 HTML 폼에서 _method=PUT
을 보내면, 실제로는 PUT
요청으로 처리됨.
2. CORS 처리 예제 (CorsFilter
)
📌 CORS(Cross-Origin Resource Sharing) 문제 해결을 위한 필터를 적용하는 방법
✅ 2.1. CorsFilter
를 사용한 CORS 처리
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*"); // 모든 도메인 허용
config.addAllowedMethod("*"); // 모든 HTTP 메서드 허용
config.addAllowedHeader("*"); // 모든 헤더 허용
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
📌 모든 도메인, 메서드, 헤더를 허용하여 CORS 문제 해결.
✅ 2.2. WebMvcConfigurer
를 사용한 CORS 설정
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**") // `/api/` 경로에만 적용
.allowedOrigins("https://example.com") // 특정 도메인 허용
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true);
}
}
📌 특정 도메인에서만 API를 호출할 수 있도록 제한 가능.
3. XSS(Cross-Site Scripting) 방어 필터
📌 XSS 공격을 방어하는 방법
- 사용자가 입력한 악성 JavaScript 코드가 실행되지 않도록 방지.
HttpServletRequestWrapper
를 사용하여 입력 값을 필터링함.
✅ XSS 방어 필터
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class XssFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
XssRequestWrapper wrappedRequest = new XssRequestWrapper((HttpServletRequest) request);
chain.doFilter(wrappedRequest, response);
}
}
✅ HttpServletRequestWrapper
를 사용하여 입력 값 필터링
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XssRequestWrapper extends HttpServletRequestWrapper {
public XssRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return sanitize(value);
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null) return null;
for (int i = 0; i < values.length; i++) {
values[i] = sanitize(values[i]);
}
return values;
}
private String sanitize(String input) {
return input == null ? null : input.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("\"", """)
.replaceAll("'", "'")
.replaceAll("&", "&");
}
}
📌 사용자가 입력한 값에서 <script>
태그와 같은 악성 코드가 실행되지 않도록 변환.
✅ XSS 필터를 Spring에 등록
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class XssFilterConfig {
@Bean
public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new XssFilter());
registrationBean.addUrlPatterns("/*"); // 모든 요청에 적용
registrationBean.setOrder(1); // 필터 실행 순서 지정
return registrationBean;
}
}
📌 모든 요청에서 XSS 공격을 자동으로 방어.
📌 정리
OncePerRequestFilter
: 한 요청당 한 번만 실행되는 필터.CorsFilter
: CORS 문제 해결.CharacterEncodingFilter
: UTF-8 인코딩 적용.HiddenHttpMethodFilter
: RESTful API 지원.- XSS 필터: 사용자의 입력 값에서
<script>
와 같은 악성 코드 차단.
🚀 필터를 적절히 사용하면 보안과 성능을 향상시킬 수 있음!