[Spring Security] SecurityConfig 리팩토링 - WebSecurityConfigurerAdapter 상속 제거, Resource Filter Chain 설정
1. Spring Security Config 리팩토링
Spring Security 버전업이 되면서 기존 프로젝트의 문제점이 생기기 시작했다. 해당 문제점에 대해 알아보고 리팩토링을 하자.
- WebSecurityConfigurerAdapter 상속 제거
- Resource Filter Chain 설정
2. 개발 환경
- Spring Boot 2.7.6
- Spring Security 5.7.6
- Java 11
- Gradle 7.5.1
3. 기존 SecurityConfig
일반적으로 많이 사용하는 SecurityConfig이다.
@Configuration
@EnableWebSecurity // Spring Security 활성화
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
// resource 에 대해 Spring Security FilterChain 제외
web.ignoring().antMatchers("/resources/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/", "/login", "/sign-up", "/check-email-token"
, "/email-login", "/check-email-login", "/login-link", "/login-by-email").permitAll()
.mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
.anyRequest().authenticated().and()
.formLogin()
.loginPage("/login").permitAll()
.logout()
.logoutSuccessUrl("/");
}
}
ant pattern을 이용한 ignore 처리가 권장되지 않는다.
해당 설정으로 실행시 하단과 같은 WARN 로그가 발생한다.
You are asking Spring Security to ignore Ant [pattern='/resource/**']. This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.
이 로그는 Spring Security 5.5.x에 추가되었으며 Spring Security Github Issue에서 확인할 수 있다. 간략히 요약해보자면,
web.ignoring()은 Spring Security 가 해당 엔드포인트에 보안헤더 또는 기타 보호 조치를 제공할 수 없다. 대신에 permitAll() 메서드를 사용하여 권한 검증 없이 요청을 보호할 수 있다. 이러한 이유때문에 해당 경고메시지를 알려주었다.
또한, static resource에 대해 SecurityFilterChain을 추가하는 방법에 대해서 알려주었는데 해당 방안은 밑에서 알아보겠습니다.
Spring Security 5.7.x 부터 WebSecurityConfigurerAdapter Deprecate 문제
Spring Security 5.7.x 부터 WebSecurityConfigurerAdapter가 Deprecated 되었다.
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
4. SecurityConfig 리팩토링
WebSecurityConfigurerAdapter를 상속하기않고 적용
WebSecurityConfigurerAdapter를 상속하지 않고 필터체인을 구성하는 방법으론 SecurityFilterChain을 Bean으로 선언하는 방법이 있다. 이때 HttpSecurity를 주입받아 사용하면 된다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().antMatchers("/resources/**");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/", "/login", "/sign-up", "/check-email-token"
, "/email-login", "/check-email-login", "/login-link", "/login-by-email").permitAll()
.mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
.anyRequest().authenticated();
http.formLogin()
.loginPage("/login")
.permitAll();
http.logout()
.logoutSuccessUrl("/");
return http.build();
}
}
WebSecurityConfigurerAdapter 상속을 제거하고 WebSecurityCustomizer 선언과 HttpSecurity의 build() 호출 후, 리턴하여 Bean으로 등록한다.
HttpSecurityConfiguration을 확인해보면 HttpSecurity에 기본적인 설정을 한 후, prototype으로 Bean을 등록한다. 따라서 매번 주입받을 때 마다 새로운 인스턴스를 주입받을 수 있다.
➤ 주의사항
WebSecurityConfigurerAdapter 상속과 SecurityFilterChain Bean 등록을 동시에 사용할 경우 아래와 같은 로그가 발생한다.
Caused by: java.lang.IllegalStateException: Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.
Resource 용 SecurityFilterChain 적용
WebSecurityCustomizer 설정을 제거하고 @Order(0) 을 추가하여 먼저 FilterChain 을 타도록 지정한다. resources(css, js 등) 의 경우 securityContext 등에 대한 조회가 불필요 하므로 disable 합니다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/", "/login", "/sign-up", "/check-email-token"
, "/email-login", "/check-email-login", "/login-link", "/login-by-email").permitAll()
.mvcMatchers(HttpMethod.GET, "/profile/*").permitAll()
.anyRequest().authenticated();
http.formLogin()
.loginPage("/login")
.permitAll();
http.logout()
.logoutSuccessUrl("/");
return http.build();
}
@Bean
@Order(0)
public SecurityFilterChain resources(HttpSecurity http) throws Exception {
return http.requestMatchers(matchers -> matchers.antMatchers( "/resources/**"))
.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll())
.requestCache(RequestCacheConfigurer::disable)
.securityContext(AbstractHttpConfigurer::disable)
.sessionManagement(AbstractHttpConfigurer::disable).build();
}
}
출처
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
https://github.com/spring-projects/spring-security/issues/10938
https://velog.io/@csh0034/Spring-Security-Config-Refactoring
'web > Spring' 카테고리의 다른 글
[Spring] ResponseEntity 와 @ResponseStatus (0) | 2023.03.30 |
---|---|
[Spring JPA] Entity의 equals와 hashCode (0) | 2023.02.28 |
[Spring] 도메인 객체 검증하는 방법 -> Validation (0) | 2022.11.18 |
[Spring] MapStruct - Entity와 DTO 매핑하기 (0) | 2022.11.07 |
[Spring] 페이징 성능 개선하기 3-2. 첫 페이지 조회 결과 캐시하기 (0) | 2022.07.21 |
댓글 개