[문제 상황]
Caused by: org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built
- 평소처럼 security 관련 설정 작업을 진행하던 도중 마주한 이 에러..
- 나는 최신 버전의 스프링 시큐리티를 사용하고 있어서 stackoverflow에서 제공하는 build() 를 지우라는 답변이 적용되지 않는 케이스였다.
- 그래서 이것저것 해보다 찾아낸 해답 ㅎ
[문제 발생 코드]
package com.yaloostore.shop.config;
import com.yalooStore.security_utils.filter.JwtAuthenticationFilter;
import com.yalooStore.security_utils.provide.JwtAuthenticationProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.client.RestTemplate;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Value("${yalooStore.auth.url}")
private String authServerUrl;
@Bean
public JwtAuthenticationProvider jwtTokenAuthenticationProvider(
RestTemplate restTemplate) {
return new JwtAuthenticationProvider(restTemplate, authServerUrl);
}
@Bean
public SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(request -> request.requestMatchers("/**").permitAll())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(
new JwtAuthenticationFilter(authenticationManager(http)),
UsernamePasswordAuthenticationFilter.class
)
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.authenticationProvider(jwtTokenAuthenticationProvider(null))
.build();
}
}
[문제 해결 코드]
package com.yaloostore.shop.config;
import com.yalooStore.security_utils.filter.JwtAuthenticationFilter;
import com.yalooStore.security_utils.provide.JwtAuthenticationProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.client.RestTemplate;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Value("${yalooStore.auth.url}")
private String authServerUrl;
@Bean
public JwtAuthenticationProvider jwtTokenAuthenticationProvider(
RestTemplate restTemplate) {
return new JwtAuthenticationProvider(restTemplate, authServerUrl);
}
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.authenticationProvider(jwtTokenAuthenticationProvider(null))
.build();
}
@Bean
public SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(request -> request.requestMatchers("/**").permitAll())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(
new JwtAuthenticationFilter(authenticationManager(http)),
UsernamePasswordAuthenticationFilter.class
)
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
}
- 간단했다 securityFilterChain을 돌려주는 해당 설정에 build가 나오니 마지막에 두어 httpSecurity.build()를 사용하는 해당 빈 주입을 먼저 해주면 된다.
- http.build()작업이 맨 아래로 오니 문제가 없이 서버가 다시 시작된다.
-This object has already been built 이미 객체가 빌드되었다는 뜻이니까 해당 객체가 빌드되기 전에 authenticationManager(HttpSecurity http)를 먼저 설정해준 다음 아래에 해당 SecurityFilterChain을 작성해주면 된다!!!