개요

Spring Security를 사용하면 쉽게 보안 구성을 할 수 있다는 장점이 있다. 하지만 대부분의 Spring Security 예시들이 구버전의 Spring을 기준으로 예시들이 작성되어 있어 최신 버전의 Spring에는 적용이 되지 않는다. Spring Security를 최신 버전의 Spring 기준에 맞추어 작성해보자.

버전 정보

Spring boot: 3.1.2
Java: 17

해결 방법

변경하고자하는 기존의 코드는 다음과 같다.

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
        .antMatchers("/A", "/B").access("hasRole('ROLE_USER'))")
        .antMatchers("/", "/**").access("permitAll")
        .and()
        .httpBasic();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
        .withUser("user1")
        .password("{noop}password1")
        .authorities("ROLE_USER")
        .and()
        .withUser("user2")
        .password("{noop}password2")
        .authorities("ROLE_USER");
    }
}

기존의 코드는 WebSecurityConfigurerAdapter를 상속한 후 Overriding하여 사용하는 방식을 사용하였는데 이제는 Bean으로 만들어 사용하는 방식을 권장하고 있다. Http security에 대한 설정을 마치고 나면 http.build()를 반환하면 된다.
requestMatchers에 대한 변화도 생겼는데 기존의 코드에서는 그냥 패턴을 String으로 표현하면 되었지만 CVE-2023-34035 취약점으로 인해 requestMatchers(MvcRequestMatcher) 또는 requestMatchers(AntPathRequestMatcher)를 사용해야 한다.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authz) ->
            authz
                .requestMatchers(antMatcher("/A"), antMatcher("/B")).hasRole("USER")
                .requestMatchers(antMatcher("/"), antMatcher("/**")).permitAll()
            )
        .formLogin(Customizer.withDefaults());
    return http.build();
}

In-memory authentication에서도 변화가 생겼다. 기존의 코드에서는 AuthenticationManagerBuilder를 주입받아 사용하였다. 이제 UserDetails라는 객체에 user의 정보를 저장하고 build한 후 InMemoryUserDetailsManager의 매개변수로 전달하면 된다.

@Bean
public UserDetailsService userDetailsService() {
    UserDetails user1 = User.builder()
            .username("user1")
            .password("{noop}password1")
            .roles("USER")
            .build();
    UserDetails user2 = User.builder()
            .username("user2")
            .password("{noop}password2")
            .roles("USER")
            .build();
    return new InMemoryUserDetailsManager(user1, user2);
}

Reference