diff --git a/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/filter/CustomFilter1.kt b/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/filter/CustomFilter1.kt new file mode 100644 index 0000000..5f1e8d5 --- /dev/null +++ b/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/filter/CustomFilter1.kt @@ -0,0 +1,31 @@ +package com.banjjoknim.playground.config.filter + +import javax.servlet.Filter +import javax.servlet.FilterChain +import javax.servlet.ServletRequest +import javax.servlet.ServletResponse + +/** + * @see javax.servlet.Filter + * @see com.banjjoknim.playground.config.security.JwtSecurityConfiguration + */ +class CustomFilter1 : Filter { + override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + println("필터1") + chain.doFilter(request, response) // 필터체인에 request, response 를 등록해주어야 한다. 그렇지 않으면 현재 필터가 진행되고 이 이후의 필터들은 더이상 동작하지 않게 된다. + } +} + +class CustomFilter2 : Filter { + override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + println("필터2") + chain.doFilter(request, response) // 필터체인에 request, response 를 등록해주어야 한다. 그렇지 않으면 현재 필터가 진행되고 이 이후의 필터들은 더이상 동작하지 않게 된다. + } +} + +class CustomFilter3 : Filter { + override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + println("필터3") + chain.doFilter(request, response) // 필터체인에 request, response 를 등록해주어야 한다. 그렇지 않으면 현재 필터가 진행되고 이 이후의 필터들은 더이상 동작하지 않게 된다. + } +} diff --git a/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/filter/FilterConfiguration.kt b/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/filter/FilterConfiguration.kt new file mode 100644 index 0000000..32e0d44 --- /dev/null +++ b/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/filter/FilterConfiguration.kt @@ -0,0 +1,60 @@ +package com.banjjoknim.playground.config.filter + +import org.springframework.boot.web.servlet.FilterRegistrationBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * 기본 설정시 Spring Security 는 일련의 Servlet Filter Chain(FilterChainProxy 라는 클래스로 등록되어 있다. 하나의 Filter 로 등록 되어있지만 내부적으로는 여러개의 Filter 가 동작하고 있다) 을 자동으로 구성한다(web tier 에 있는 Spring Security 는 Servlet Filter 에 기반을 두고 있다). + * + * 일반적인 웹 환경에서 브라우저가 서버에게 요청을 보내게 되면, DispatcherServlet(Controller)가 요청을 받기 이전에 많은 ServletFilter(서블릿 필터)를 거치게 된다. + * + * Spring Security 역시 Servlet Filter 로써 작동하며, 인증 또는 권한과 관련한 처리를 진행하게 된다. + * + * 본래 Servlet Filter 는 WAS(Web Application Server)에서 담당하는데 Spring 은 이 Servlet Filter 들을 직접 관리하기 위해서 DelegatingFilterProxy 를 web.xml 에 설정한다. + * + * 이를 통해 Spring 에서 설정된 Servlet Filter Bean 객체를 거치게 된다. + * + * 여기서는 스프링 시큐리티 필터체인에 필터를 추가하는 대신, 직접 필터를 만들어서 사용한다. + * + * 굳이 Security Filter Chain 에 필터를 추가할 필요가 없고, 이렇게 따로 만들어서 사용해도 된다. + * + * Filter 를 Bean 으로 등록해놓으면, 요청이 들어왔을 때 등록된 Filter 가 동작하게 된다. + * + * 이때, Security Filter Chain 이 우리가 직접 만든 Filter 보다 먼저 동작한다. + * + * 만약 우리가 만든 Filter 를 원하는 위치에서 동작하도록 하고 싶다면 원하는 위치에 Filter 를 추가하면 된다. + * + * 이때, Security Filter Chain 의 순서는 com.banjjoknim.playground.config.filter.SecurityFilterChain.png 이미지를 참고하자. + * + * - Filter type의 Bean에는 @Order 어노테이션으로 순서를 정할 수 있다. + * - FilterRegistrationBean을 이용하여 순서를 정할 수 있다 + * + * ```kotlin + * http.addFilterBefore(MySecurityFilter3(), SecurityContextPersistenceFilter::class.java) + * ``` + * + * @see org.springframework.boot.web.servlet.FilterRegistrationBean + * @see com.banjjoknim.playground.config.security.JwtSecurityConfiguration + * @see org.springframework.web.filter.DelegatingFilterProxy + * @see org.springframework.security.web.FilterChainProxy + */ +@Configuration +class FilterConfiguration { + + @Bean + fun customFilter1(): FilterRegistrationBean { + val bean = FilterRegistrationBean(CustomFilter1()) + bean.addUrlPatterns("/*") // 모든 요청에 대해 필터가 동작하도록 설정한다. + bean.order = 0 // 필터의 순서를 정할 수 있는데, 낮은 번호가 필터중에서 가장 먼저 실행된다. + return bean + } + + @Bean + fun customFilter2(): FilterRegistrationBean { + val bean = FilterRegistrationBean(CustomFilter2()) + bean.addUrlPatterns("/*") // 모든 요청에 대해 필터가 동작하도록 설정한다. + bean.order = 1 // 필터의 순서를 정할 수 있는데, 낮은 번호가 필터중에서 가장 먼저 실행된다. + return bean + } +} diff --git a/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/filter/SecurityFilterChain.png b/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/filter/SecurityFilterChain.png new file mode 100644 index 0000000..fbba9d5 Binary files /dev/null and b/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/filter/SecurityFilterChain.png differ diff --git a/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/security/JwtSecurityConfiguration.kt b/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/security/JwtSecurityConfiguration.kt index 77ad529..e2313c0 100644 --- a/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/security/JwtSecurityConfiguration.kt +++ b/놀이터(예제 코드 작성)/spring-security/src/main/kotlin/com/banjjoknim/playground/config/security/JwtSecurityConfiguration.kt @@ -1,9 +1,11 @@ package com.banjjoknim.playground.config.security +import com.banjjoknim.playground.config.filter.CustomFilter3 import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.web.context.SecurityContextPersistenceFilter import org.springframework.web.filter.CorsFilter /** @@ -11,6 +13,11 @@ import org.springframework.web.filter.CorsFilter * * 토큰 기반(JWT)을 사용한 설정에서는 기본이며, 상태가 없는 서버를 만들 때 사용한다. * + * ```kotlin + * Spring Filter Chain 에 존재하는 BasicAuthenticationFilter의 동작 이전에 MySecurityFilter1 을 추가한다. 하지만 반드시 SecurityFilter 에 Filter 를 추가할 필요는 없다. + * + * http.addFilterBefore(MySecurityFilter1(), BasicAuthenticationFilter::class.java) + * ``` * @see org.springframework.security.config.http.SessionCreationPolicy */ @EnableWebSecurity // 시큐리티 활성화 -> 시큐리티 설정을 기본 스프링 필터체인에 등록한다. @@ -18,6 +25,13 @@ class JwtSecurityConfiguration( private val corsFilter: CorsFilter // CorsConfiguration 에서 Bean 으로 등록해준 CorsFilter 를 Spring 으로부터 DI 받는다. ) : WebSecurityConfigurerAdapter() { override fun configure(http: HttpSecurity) { + + // Spring Filter Chain 에 존재하는 BasicAuthenticationFilter의 동작 이전에 MySecurityFilter1 을 추가한다. 하지만 반드시 SecurityFilter 에 Filter 를 추가할 필요는 없다. +// http.addFilterBefore(MySecurityFilter1(), BasicAuthenticationFilter::class.java) + + // 우리가 원하는 위치에 Filter 를 등록한다. 만약 Spring Security Filter 보다도 먼저 실행되게 하고 싶다면 SecurityContextPersistenceFilter 보다 먼저 실행되도록 아래처럼 등록해주면 된다. + http.addFilterBefore(CustomFilter3(), SecurityContextPersistenceFilter::class.java) + http.csrf().disable() // 기본적으로 웹은 STATELESS 인데, STATEFUL 처럼 쓰기 위해서 세션과 쿠키를 만든다. 이때, 그걸(세션과 쿠키) 사용하지 않도록 설정하는 것이다. http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 세션을 사용하지 않겠다는 설정. 토큰 기반에서는 기본 설정이다. 상태가 없는 서버를 만든다.