개발 일지

[Spring Security] WebSecurityConfigurerAdapter is deprecated 본문

Spring/Spring Security

[Spring Security] WebSecurityConfigurerAdapter is deprecated

junjun_ 2022. 12. 18. 17:46

jwt를 이용한 로그인을 구현하기 위해 예전에 사용했던 Security설정을 복붙 했더니  'org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter' is deprecated 이란 문구가 뜨면서 WebSecurityConfigurerAdapter을 extends 할 수 없는 이슈가 있었습니다. 

 

Deprecated
Use a org.springframework.security.web.SecurityFilterChain Bean to configure HttpSecurity or a WebSecurityCustomizer Bean to configure WebSecurity

더 이상 사용되지 않음
org.springframework.security.web.SecurityFilterChain Bean을 사용하여 HttpSecurity를 ​​구성하거나 WebSecurityCustomizer Bean을 사용하여 WebSecurity를 ​​구성하십시오.

 

해당 이슈에 대한 내용은 공식문서 에 정리되어 있습니다.

 

결론부터 얘기하면

  • Spring Security 5.7.0-M2 부터는 구성 요소 기반 보안 설정을 사용하도록 권장
  • 더 이상  WebSecurityConfigurerAdapter을 사용되지 않는다고 합니다.

before(이제 사용되지 않음)

  • HttpSecurity 구성할때  WebSecurityConfigurerAdapter 상속 후, configure 메서드를 오버라이딩 하여 설정하는 방식

After

  •  SecurityFilterChain를 빈으로 등록하는 방식을 권장

 


 

SecurityFilterChain 을 빈으로 등록

공식문서에는 다음과 같이 설명되어 있습니다.

In Spring Security 5.7.0-M2 we deprecated the WebSecurityConfigurerAdapter, as we encourage users to move towards a component-based security configuration.
To assist with the transition to this new style of configuration, we have compiled a list of common use-cases and the suggested alternatives going forward.
In the examples below we follow best practice by using the Spring Security lambda DSL and the method 
HttpSecurity#authorizeHttpRequests to define our authorization rules

Configuring HttpSecurity
In Spring Security 5.4 we introduced the ability to configure HttpSecurity by creating a SecurityFilterChain bean.

 

간단하게 정리하자면HttpSecurity 구성할때  WebSecurityConfigurerAdapter 상속 후, configure 메서드를 오버라이딩 하여 설정하는 방식을 사용하지 않고 앞으로는 SecurityFilterChain를 빈으로 등록하는 방식을 권장한다는 것입니다.

 

아래는 WebSecurityConfigurerAdapterHTTP Basic으로 모든 엔드포인트를 보호하는 를 사용하는 구성의 예입니다.

 

before

  • WebSecurityConfigurerAdapter 상속 후, configure 메서드를 오버라이딩 방식
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(withDefaults());
    }

}

앞으로 이 작업을 수행하는 권장 방법은 SecurityFilterChainbean을 등록하는 것입니다.

 

after

  •  SecurityFilterChain을 bean으로 등록하는 방식
@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(withDefaults());
        return http.build();
    }

}

 

코드 자체는 많이 달라지지 않았습니다. 차이점은 

  • @Bean 어노테이션을 이용하여 빈으로 등록한 것과
  • return 반환 값이 있다는 것입니다.

 

결과적으로 원래였으면 상속받아서 오버라이드를 통해 사용했어야 했던 것들을 @Bean 애너테이션을 통해 스프링 빈으로 등록해 주면서 spring에서 자동으로 적용시켜주는 것을 알 수 있습니다.

 

.addFilter 

SecurityFilterChain에 필터(JwtAuthenticationFilter)를 등록할 때 WebSecurityConfigurerAdapter가 빈으로 갖고 있는 authenticationManager 빈을 생성자를 통해 JwtAuthenticationFilter에 주입시켰으나 WebSecurityConfigurerAdapter가 deprecated되면서authenticationManager 빈을 작성해 줘야합니다.

 

bfore

  • authenticationManager () 으로 바로 주입가능
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), userRepository))

 

After

  • authenticationManager 빈 작성 후
  • 작성한 빈을 통해 주입시켜줘야 한다
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
    return authenticationConfiguration.getAuthenticationManager();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            .csrf().disable()
            .httpBasic().disable() /*JWT 이외의  session, http basic, loginform 등의 인증방식을 제거 */
            .formLogin().disable()
            .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeRequests(authorizeRequests ->
                    authorizeRequests
                            .antMatchers("/api/v1/sign-up").permitAll()
                            .antMatchers("/api/v1/login").permitAll()
                            .anyRequest().authenticated()
            )
            .addFilter(new JwtAuthenticationFilter(authenticationManager(http.getSharedObject(AuthenticationConfiguration.class)), jwtUtil, mapper))
            .addFilter(new JwtAuthorizationFilter(authenticationManager(http.getSharedObject(AuthenticationConfiguration.class)), userDetailsService, jwtUtil));
    return http.build();
}

 

 

Spring Security Config 최신화 적용하기

 

예전 SecurityConfig.java 코드

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final CorsConfig corsConfig;
    private final UserRepository userRepository;
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;


    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers(
                        "/h2-console/**"
                        ,"/favicon.ico"
                        ,"/error"
                );
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilter(corsConfig.corsFilter())
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .formLogin().disable()
                .httpBasic().disable()
                .addFilter(new JwtAuthenticationFilter(authenticationManager()))
                .addFilter(new JwtAuthorizationFilter(authenticationManager(), userRepository))
                .authorizeRequests()
                .antMatchers("/api/v1/user/**")
                .access("hasRole('ROLE_USER') or hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')")
                .antMatchers("/api/v1/manager/**")
                .access("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')")
                .antMatchers("/api/v1/admin/**")
                .access("hasRole('ROLE_ADMIN')")
                .anyRequest().permitAll();
    }

 

 

바꾼 코드

public class SecurityConfig  {
    
    private final AuthenticationFailureHandler authenticationFailureHandler;
    private final AuthenticationSuccessHandler authenticationSuccessHandler;
    private final ObjectMapper mapper;
    private final CustomUserDetailService userDetailsService;
    private final JwtUtil jwtUtil;


    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

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

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        AuthenticationManager authenticationManager = authenticationManager(http.getSharedObject(AuthenticationConfiguration.class));

        JwtAuthenticationFilter jwtAuthenticationFilter
                = new JwtAuthenticationFilter(authenticationManager, mapper);
        jwtAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);
        jwtAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);

        http
                .csrf().disable()
                .httpBasic().disable() /*JWT 이외의  session, http basic, loginform 등의 인증방식을 제거 */
                .formLogin().disable()
                .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests
                                .antMatchers("/api/v1/sign-up").permitAll()
                                .antMatchers("/api/v1/login").permitAll()
                                .anyRequest().authenticated()
                )
                .addFilter(jwtAuthenticationFilter)
                .addFilter(new JwtAuthorizationFilter(authenticationManager, userDetailsService, jwtUtil));
        return http.build();
    }

 

정리

제가 필요한 설정 위주로 정리하였기 때문에 더 자세한 내용은 공식문서를 확인하면 좋을 것 같습니다.

기술은 계속해서 추가되고, 바뀌기  때문에 공부는 계속되어야겠습니다.. ㅎㅎ