Back-End/에러와의 전쟁

[Error] - Caused by: org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built

얄루몬 2023. 7. 10. 21:20

[문제 상황]

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을 작성해주면 된다!!!