본문 바로가기

[SPRING]/Security

The type WebSecurityConfigurerAdapter is deprecated

반응형

The type WebSecurityConfigurerAdapter is deprecated

더 이상 WebSecurityConfigurerAdapter가 사용되지 않습니다..

잉?

간만에 스프링 부트로 시큐리티 설정하는데

public class WebSecurityConfig extends WebSecurityConfigurerAdapter
취소선이 따악!

The type WebSecurityConfigurerAdapter is deprecated라고 떠 있었다..

구글링해서 몇가지 글들이 있었지만 언제나 공식문서가 정답이었기에 공식문서 읽는 습관을 들이자....

원인

2022년 2월 21일 스프링 공식문서
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
에 따르면

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

사용자가 구성 요소 기반 보안 구성으로 이용하기를 권장하므로, 더 이상 WebSecurityConfigurerAdapter를 지원하지 않는다고 한다.

해결

변경된 것들이 많다... 이번 기회에 한 번 쭉 읽어서 알아보고! 쓸 일이 생길 때마다 다시 찾아보며 습득해야겠다.

  • HttpSecurity 설정
  • WebSecurity 설정
  • LDAP Authentication
  • JDBC Authentication
  • In-Memory Authentication
  • Global AuthenticationManager
  • Local AuthenticationManager
  • Accessing the local AuthenticationManager

HttpSecurity 설정

Spring Security 5.4부터는..
SecurityFilterChainbean생성을 이용해 HttpSecurity를 설정하는것을 소개하고있다.

기존
WebSecurityConfigurerAdapter 이용

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

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

}

변경
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();
    }

}

WebSecurity 설정

Spring Security 5.4에서 WebSecurityCustomizer를 소개했었다.
WebSecurityCustomizer는 callback 인터페이스로 WebSecurity를 커스텀하는데 유용하다.

기존
WebSecurityConfigurerAdapter 이용

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/ignore1", "/ignore2");
    }

}

변경
WebSecurityCustomizer baen 이용

@Configuration
public class SecurityConfiguration {

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
    }

}

주의
이렇게 WebSecurity의 요청을 무시하는것보다는
HttpSecurity#authorizeHttpRequestspermitAll을 이용하는것을 추천한다.

LDAP Authentication

Spring Security 5.7에서

  • EmbeddedLdapServerContextSourceFactoryBean
  • LdapBindAuthenticationManagerFactory
  • LdapPasswordComparisonAuthenticationManagerFactory
    3가지 기능이 추가되었고, 이 기능들은 내장된 LDAP서버를 생성하고 LDAP 인증을 수행하는데 사용 가능하다.

기존
WebSecurityConfigurerAdapter이용

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .ldapAuthentication()
            .userDetailsContextMapper(new PersonContextMapper())
            .userDnPatterns("uid={0},ou=people")
            .contextSource()
            .port(0);
    }

}

변경
WebSecurityConfigurerAdapter로 내장 LDAP 생성,
AuthenticationManager로 LDAP 인증

@Configuration
public class SecurityConfiguration {
    @Bean
    public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
        EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean =
            EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
        contextSourceFactoryBean.setPort(0);
        return contextSourceFactoryBean;
    }

    @Bean
    AuthenticationManager ldapAuthenticationManager(
            BaseLdapPathContextSource contextSource) {
        LdapBindAuthenticationManagerFactory factory =
            new LdapBindAuthenticationManagerFactory(contextSource);
        factory.setUserDnPatterns("uid={0},ou=people");
        factory.setUserDetailsContextMapper(new PersonContextMapper());
        return factory.createAuthenticationManager();
    }
}

JDBC Authentication

기존
WebSecurityConfigurerAdapter이용

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .build();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        auth.jdbcAuthentication()
            .withDefaultSchema()
            .dataSource(dataSource())
            .withUser(user);
    }
}

변경
JdbcUserDetailsManager bean으로 인증

@Configuration
public class SecurityConfiguration {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
            .build();
    }

    @Bean
    public UserDetailsManager users(DataSource dataSource) {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
        users.createUser(user);
        return users;
    }
}

주의
예제로 가독성을 위헤 User.withDefaultPasswordEncoder()를 이용했는데 실제배포(프로덕션)에서는 외부에서 비밀번호를 해싱하는 것(ex.Spring Boot CLI 이용 등)이 좋다!.
참조:https://docs.spring.io/spring-security/reference/features/authentication/password-storage.html#authentication-password-storage-boot-cli

In-Memory Authentication

기존
WebSecurityConfigurerAdapter이용

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        auth.inMemoryAuthentication()
            .withUser(user);
    }
}

변경
InMemoryUserDetailsManager bean 이용

@Configuration
public class SecurityConfiguration {
    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user);
    }
}

주의
이 예제도 JDBC Authentication와 마찬가지로....
예제로 가독성을 위헤 User.withDefaultPasswordEncoder()를 이용했는데 실제배포(프로덕션)에서는 외부에서 비밀번호를 해싱하는 것(ex.Spring Boot CLI 이용 등)이 좋다!.
참조:https://docs.spring.io/spring-security/reference/features/authentication/password-storage.html#authentication-password-storage-boot-cli

Global AuthenticationManager

글로벌로 AuthenticationManager를 만드려면 AuthenticationManagerbean으로 등록하기만하면 된다.
예제는 위의 LDAP인증과 같다.

Local AuthenticationManager

Spring Security 5.6에서 AuthenticationManager를 override해 SecurityFilterChain을 구체화하는 HttpSecurity#authenticationManager를 소개했다...

커스텀 AuthenticationManager를 설정하는 예제

@Configuration
public class SecurityConfiguration {

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

}

Accessing the local AuthenticationManager

Local AuthenticationManagercustomDSL에서 접근이 가능하다.

참조 : customDSL은?
https://docs.spring.io/spring-security/reference/5.7.0-M2/servlet/configuration/java.html#jc-custom-dsls

예제

public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
        http.addFilter(new CustomFilter(authenticationManager));
    }

    public static MyCustomDsl customDsl() {
        return new MyCustomDsl();
    }
}

SecurityFilterChain의 구성을 이용해 customDSL을 적용하는 예제

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // ...
    http.apply(customDsl());
    return http.build();
}
반응형