커스텀 필터 클래스를 만들려면 Filter 인터페이스의 doFilter()를 재정의해야 한다.
filterChain.doFilter(servletRequest,servletResponse);
는 필터체인에 등록되있는 다음 필터를 호출하는 코드이다.
import javax.servlet.*;
import java.io.IOException;
public class MyFilter1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter 1");
filterChain.doFilter(servletRequest,servletResponse);
}
}
public class MyFilter2 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter 2");
filterChain.doFilter(servletRequest,servletResponse);
}
}
커스텀 필터를 필터체인에 등록하는 방법 중 하나는 @Configuration
으로 등록한 필터 설정용 클래스에 빈으로 등록하는 것이다.
FilterRegistrationBean<T>
를 빈으로 등록하면 되는데, 제네릭 T에 커스텀 필터를 넣으면 된다.
커스텀 필터는 기본적으로 시큐리티 필터보다 후순위이다.
WebSecurityConfigurerAdapter
상속체의 configure(HttpSecurity http)
메서드에서 addFilterBefore()등을 이용하면 시큐리티 필터보다 순위를 높게 가질 수 있다.
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
//커스텀 필터를 필터체인에 등록하는 설정용 클래스
//기본적으로 커스텀 필터는 스프링 시큐리티 필터체인보다 나중에 발동된다.
@Bean
public FilterRegistrationBean<MyFilter1> filter1(){
FilterRegistrationBean<MyFilter1>bean=new FilterRegistrationBean<>(new MyFilter1());
bean.addUrlPatterns("/*");//모든 요청에 대해 필터를 거쳐라.
bean.setOrder(0);//필터의 우선순위. 0에 가까울 수록 높음
return bean;
}
@Bean
public FilterRegistrationBean<MyFilter2> filter2(){
FilterRegistrationBean<MyFilter2>bean=new FilterRegistrationBean<>(new MyFilter2());
bean.addUrlPatterns("/*");//모든 요청에 대해 필터를 거쳐라.
bean.setOrder(1);//필터의 우선순위. 0에 가까울 수록 높음
return bean;
}
}
먼저 다음과 같이 커스텀 필터를 만든다.
public class MyFilter3 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest)servletRequest;
HttpServletResponse res=(HttpServletResponse)servletResponse;
if (req.getMethod().equals("POST")){
String headerAuth=req.getHeader("Authorization");
//토큰 : cors 이걸 만들어서 줘야 한다.
//id,pw가 정상적으로 들어와서 로그인이 완료되면 토큰을 만들어주고 그걸로 응답을 해준다.
//요청할 때마다 header의 Authroization에 value 값으로 토큰을 가지고 온다.
//그 토큰이 넘어오면 이 토큰이 내가 만든 토큰이 맞는지만 검증만 하면 된다.
if (headerAuth.equals("cors")){
filterChain.doFilter(servletRequest,servletResponse);
}
else{
System.out.println("filter 3");
PrintWriter outPrintWriter=res.getWriter();
outPrintWriter.println("인증 안됨.");
}
}
}
}
위 커스텀 필터는 request의 HTTP 메서드가 POST 요청이면 가로채서 헤더 부분을 파악한다. 만약 헤더에 Authorization 이라는 키의 value(→headerAuth)를 얻어낸다.
이 때 value가 서버에 있는 토큰 값과 같다면, 인증을 해준 사용자가 맞기 때문에 다음 필터체인으로 넘어간다.
그렇지 않으면 다른 사용자이므로 인증이 필요하다. 따라서 다음 필터로 넘어가지 못하게 하였다.
이 커스텀 필터를 스프링 시큐리티가 제공하는 필터보다 우선 순위를 높게 하려면 설정용 클래스에서 addFilterBefore()를 이용하면 된다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
....
.and()
.addFilterBefore(new MyFilter3(), SecurityContextHolderAwareRequestFilter.class)
....
}
}
아래와 같이 헤더에 Authorization : cors 라 적고 포스트 요청을 하게 되면 커스텀 필터가 먼저 걸러내는 것을 확인할 수 있다.