카테고리 없음

Deprecated된 WebSecurityConfigurerAdapter 없이 CustomSecurityConfig 구현하기

맘마미아1 2022. 6. 11. 20:02

In Spring Security 5.7.0-M2 we deprecated the WebSecurityConfigurerAdapter, as we encourage users to move towards a component-based security configuration.

스프링시큐리티 5.7.0버전 이후로 WebSecurityConfigurerAdapter가 Deprecated되었습니다

참고하시라고 소스코드 올려봅니다.

 

참고로 예제소스는 남가람북스 구멍가게코딩단의 코드로배우는 웹 스프링부트 프로젝트 입니다.

 

 

AS-IS 소스

@Configuration
@Log4j2
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private ClubUserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
        http.formLogin();
        http.csrf().disable();
        http.logout();
        http.oauth2Login().successHandler(successHandler());
        http.rememberMe().tokenValiditySeconds(60*60*7).userDetailsService(userDetailsService);  //7days

        http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(apiLoginFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public ApiLoginFilter apiLoginFilter() throws Exception{

        ApiLoginFilter apiLoginFilter =  new ApiLoginFilter("/api/login", jwtUtil());
        apiLoginFilter.setAuthenticationManager(authenticationManager());

        apiLoginFilter
                .setAuthenticationFailureHandler(new ApiLoginFailHandler());

        return apiLoginFilter;
    }

    @Bean
    public JWTUtil jwtUtil() {
        return new JWTUtil();
    }

    @Bean
    public ApiCheckFilter apiCheckFilter() {

        return new ApiCheckFilter("/notes/**/*", jwtUtil());
    }



    @Bean
    public ClubLoginSuccessHandler successHandler() {
        return new ClubLoginSuccessHandler(passwordEncoder());
    }

 

 

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin();
        http.csrf().disable();
        http.logout();
        http.oauth2Login().successHandler(successHandler());
        http.rememberMe().tokenValiditySeconds(60*60*7).userDetailsService(userDetailsService);  //7days

        http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(apiLoginFilter(), UsernamePasswordAuthenticationFilter.class);
    }

configure 메서드는 공식 홈페이지에서 발표한대로 SecurityFilterChain를 리턴타입으로 받아서 그대로 작성하면 됩니다.

이전 소스
Deprecated 이후

----------->

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.formLogin();
        http.csrf().disable(); 
        http.logout();

        http.oauth2Login().successHandler(successHandler());
        http.rememberMe().tokenValiditySeconds(60*60*7)
                .userDetailsService(userDetailsService);

        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);

        http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(apiLoginFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

 

 

 

@Bean
public ApiLoginFilter apiLoginFilter() throws Exception {
    ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
    apiLoginFilter.setAuthenticationManager(authenticationManager());
    return apiLoginFilter;
}

다만 authenticationManager()같은 메서드를 받아오는 경우엔 어떻게 작성을 해야할까요 ?

 

처음 삽질할땐 이런방식을 해봤습니다.

 

@Bean
public ApiLoginFilter apiLoginFilter(HttpSecurity http) throws Exception {
    ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
    apiLoginFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
    return apiLoginFilter;
}
private AuthenticationManager authenticationManager;
@Bean
public ApiLoginFilter apiLoginFilter() throws Exception {
    ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
    apiLoginFilter.setAuthenticationManager(authenticationManager);
    return apiLoginFilter;
}

위 소스의 결과는 

authenticationManager must be specified

이러한 에러가 나게됩니다.

 

-------------해결-----------------

 

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // ....

    AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);

    http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
    http.addFilterBefore(apiLoginFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);

    return http.build();
}

 

AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);

를 filterChain으로 이동시켜주고 apiLoginFilter에 authenticationManger를 전달해줬습니다.

 

@Bean
public ApiLoginFilter apiLoginFilter(AuthenticationManager authenticationManager) throws Exception {
    ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
    apiLoginFilter.setAuthenticationManager(authenticationManager);
    return apiLoginFilter;
}

apiLoginFilter에 파라미터로 받은 Authentication를 setAuthenticationManager에 주입시킵니다.

 
Description:

Parameter 0 of method apiLoginFilter in org.geon.club.config.SecurityConfig required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.


Action:

Consider defining a bean of type 'org.springframework.security.authentication.AuthenticationManager' in your configuration.

AuthenticationManager 가 SecurityConfig에 Bean으로 등록되어있지 않다는 에러로

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
   return authenticationConfiguration.getAuthenticationManager();
}

AuthenticationManager를 Bean으로 등록시켜줍니다.

 

 ------------최종소스------------

 

TO-BE 소스

@Configuration
@Log4j2
@RequiredArgsConstructor
public class SecurityConfig {

    private final ClubUserDetailService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.formLogin();
        http.csrf().disable();
        http.logout();

        http.oauth2Login().successHandler(successHandler());
        http.rememberMe().tokenValiditySeconds(60*60*7)
                .userDetailsService(userDetailsService);

        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);

        http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(apiLoginFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public ClubLoginSuccessHandler successHandler() {
        return new ClubLoginSuccessHandler(passwordEncoder());
    }

    @Bean
    public ApiCheckFilter apiCheckFilter() {
        return new ApiCheckFilter("/notes/**/*");
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public ApiLoginFilter apiLoginFilter(AuthenticationManager authenticationManager) throws Exception {
        ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
        apiLoginFilter.setAuthenticationManager(authenticationManager);
        apiLoginFilter.setAuthenticationFailureHandler(new ApiLoginFailHandler());

        return apiLoginFilter;
    }

    @Bean
    public JWTUtil jwtUtil() {
        return new JWTUtil();
    }

 

참고자료

https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter