개발공부/Spring
[2023.06.10 수정] Spring Filter 도입하기
원석💎-dev
2022. 5. 15. 14:56
반응형
1. Filter와 OncePerRequestFilter의 차이
- Servlet은 사용자의 요청을 받으면, Servlet을 생성해 메모리에 저장해 두고, 같은 클라이언트의 요청을 받으면 생성해둔 Servlet객체를 재활용하여 요청을 처리한다.
- Spring (주로 Spring Security)에서는 RequestDispatcher 클래스를 이용해 사용자의 요청에 의해 생성된 Servlet에서 다른 Servlet으로 dispatch하는 경우가 있는데, 이때 Filter가 두번 실행된다.
- OncePerRequestFilter의 경우 사용자 요청 당 한 번의 Filter만 적용되는 것이 보장된다.
참고 글 :
Spring Security에서 사용되는 RequestDispatcher [https://dev-ppyong.tistory.com/12]
Filter와 OncePerRequestFilter의 차이 [https://minkukjo.github.io/framework/2020/12/18/Spring-142/]
RequestDispatcher와 HttpServletResponse#sendRedirect의 차이 [https://dololak.tistory.com/502]
2. Requset Header, Body 내용 변경 및 추가하기
- OncePerRequestFilter에서는 doFilterInternal() 함수를 이용해 servlet Request, Response Filter를 수행한다.
- Filter를 사용하는 것은 Request 또는 Reponse의 값의 body나 header 값을 검증, 변경, 추가하는 목적이 있다. 하지만 HttpServletRequest 객체의 값은 직접 수정할 수 없어, HttpServletRequestWrapper, HttpServletResponseWrapper을 상속받아 직접 구현할 수 있다.
- getHeader의 return이 not null이 되게 할 수 있지만, spring boot를 사용한다면 기본 필터가 적용되어 사용하지 않는 header를 호출할 수 있다. 그래서 getHeader를 사용할 때 null이 반환이 가능해야 오류가 발생하지 않는다.
- header의 값의 직접 수정이나 추가를 지양하자.
- 함수로 받아온 변수를 직접 수정하는 일은 거의 없다. 헤더도 마찬가지로 클라이언트가 보내온 변수로 여기고 수정을 지양하자.
- Requset Header에 값 추가하기 (Spring Boot, Kotlin)
class RequestHeaderWrapper(request: HttpServletRequest) : HttpServletRequestWrapper(request) {
private val headerMap = mutableMapOf<String, String?>()
fun addHeader(name: String, value: String?) { headerMap[name] = value }
override fun getHeader(name: String): String? {
var headerValue = super.getHeader(name)
if (headerMap.containsKey(name)) {
headerValue = headerMap[name]
}
return headerValue
}
override fun getHeaderNames(): Enumeration<String> {
val names: List<String> = Collections.list(super.getHeaderNames()).apply {
headerMap.forEach { name -> this.add(name.key) }
}.distinct()
return Collections.enumeration(names)
}
override fun getHeaders(name: String): Enumeration<String?> {
val values: List<String?> = Collections.list(super.getHeaders(name)).apply {
if (headerMap.containsKey(name)) {
this.add(headerMap[name])
}
}
return Collections.enumeration(values)
}
}
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain
) {
val accessToken = "testAccessToken"
val requestWrapper = if (accessToken.isNullOrEmpty()) {
request
} else {
val requestWrapper = RequestHeaderWrapper(request)
requestWrapper.addHeader("X-Requester-Access-Token", accessToken)
requestWrapper
}
filterChain.doFilter(requestWrapper, response)
}
참고 글 :
Requset Header 추가하기 [http://makble.com/adding-http-headers-to-requests-in-filters-and-servlets]
3. Filter chain의 순서 정하기 (+ Spring Security Filter)
- FilterRegistrationBean에 순서(Order)설정 말고도 다양한 세팅을 해줄 수 있다.
@Configuration
public class ServletConfig {
@Bean
public firstFilter(): FilterRegistrationBean<FirstFilter> {
val registrationBean = FilterRegistrationBean(FirstFilter())
registrationBean.setOrder(1);
return registrationBean;
}
@Bean
public firstFilter(): FilterRegistrationBean<SecondFilter>{
val registrationBean = FilterRegistrationBean(SecondFilter())
registrationBean.setOrder(2);
return registrationBean;
}
}
- Spring Security를 사용할 경우 자동으로 제공해주는 Filter들이 존재한다.
- Spring Secuirty가 제공하는 Filter 확인하기 위해 @EnableWebSecurity(debug = true)를 붙이고 실행하면, Security의 Filter목록이 log로 출력된다.
@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
- Spring Secuirty가 제공하는 Filter보다 사용자 정의 Filter를 먼저 사용하고 싶다면 application.yaml 파일에 spring.security.filter.order=10을 추가해준다. spring security Filter앞에 10개의 사용자 정의 Filter를 추가할 수 있다.
참고 글 :
FilterChain이란? [https://dololak.tistory.com/599]
Filter 순서 설정하기 [https://www.baeldung.com/spring-boot-add-filter]
Spring Security Filter chain 확인 [https://vsh123.github.io/spring%20security/Spring-Security-Filter-chain/]
Spring Security Filter 보다 사용자 정의 Filter 먼저 사용 [https://12teamtoday.tistory.com/141]
반응형