WebSecurityConfigurerAdapter 대신 Securityfilterchain + WebSecurityCustomizer로 직접 커스터마이징 하기.

Yu Seong Kim·2024년 4월 1일
4

SpringBoot

목록 보기
18/29
post-thumbnail

WebSecurityConfigurerAdapter 지원중단

WebSecurityConfigurerAdapter가 스프링시큐리티 5.7x 부터 지원중단 한다는 것을 보았습니다. 궁금증을 가지고, 구글링을 한 결과 실제 지원중단은 아니고 SecurityFilterChain Bean을 정의하는 것으로 권장하고 있다는 것을 확인하였습니다. 그 이유는 SecurityFilterChain Bean을 정의하는 것이 WebSecurityConfigurerAdapter를 상속받는 방식보다 더 명시적이고, 커스터마이징하기 쉬울 뿐만 아니라 필요한 보안 구성만을 선별해 적용할 수 있게 해주기 때문입니다.
그래서 저는 이 부분을 멋쟁이사자처럼12기 시큐리티 세션을 진행하면서 설명하기로 마음 먹었습니다.

기존 WebSecurityConfigurerAdapter를 이용한 SecurityConfiguration

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtTokenProvider jwtTokenProvider;

    @Autowired
    public SecurityConfig(JwtTokenProvider jwtTokenProvider){
        this.jwtTokenProvider = jwtTokenProvider;
    }
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.httpBasic().disable() // REST API는 UI를 사용하지 않으므로 기본설정을 비활성화

                .csrf().disable() // REST API는 csrf 보안이 필요 없으므로 비활성화

                .sessionManagement()
                .sessionCreationPolicy(
                        SessionCreationPolicy.STATELESS) // JWT Token 인증방식으로 세션은 필요 없으므로 비활성화

                .and()
                .authorizeRequests() // 리퀘스트에 대한 사용권한 체크
                .antMatchers("/sign-api/sign-in", "/sign-api/sign-up",
                        "/sign-api/exception").permitAll() // 가입 및 로그인 주소는 허용
                .antMatchers(HttpMethod.GET, "/board-api/**").permitAll() // product로 시작하는 Get 요청은 허용

                .antMatchers("**exception**").permitAll()

                .anyRequest().hasRole("ADMIN") // 나머지 요청은 인증된 ADMIN만 접근 가능

                .and()
                .exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler())
                .and()
                .exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())

                .and()
                .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
                        UsernamePasswordAuthenticationFilter.class); // JWT Token 필터를 id/password 인증 필터 이전에 추가
    }

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

    @Override
    public void configure(WebSecurity webSecurity) {
        webSecurity.ignoring().antMatchers("/v2/api-docs", "/swagger-resources/**",
                "/swagger-ui.html", "/webjars/**", "/swagger/**", "/sign-api/exception");
    }



}

이 방법을 대신하여 SecurityFilterChain Bean을 직접 정의하는 코드로 변경해 보겠습니다. 방법은 간단합니다.


@Configuration
public class SecurityConfig {

    private final JwtTokenProvider jwtTokenProvider;

    @Autowired
    public SecurityConfig(JwtTokenProvider jwtTokenProvider){
        this.jwtTokenProvider = jwtTokenProvider;
    }
    @Bean
    protected SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.httpBasic().disable() // REST API는 UI를 사용하지 않으므로 기본설정을 비활성화

                .csrf().disable() // REST API는 csrf 보안이 필요 없으므로 비활성화

                .sessionManagement()
                .sessionCreationPolicy(
                        SessionCreationPolicy.STATELESS) // JWT Token 인증방식으로 세션은 필요 없으므로 비활성화

                .and()
                .authorizeRequests() // 리퀘스트에 대한 사용권한 체크
                .antMatchers("/sign-api/sign-in", "/sign-api/sign-up",
                        "/sign-api/exception").permitAll() // 가입 및 로그인 주소는 허용
                .antMatchers(HttpMethod.GET, "/board-api/**").permitAll() // product로 시작하는 Get 요청은 허용

                .antMatchers("**exception**").permitAll()

                .anyRequest().hasRole("ADMIN") // 나머지 요청은 인증된 ADMIN만 접근 가능

                .and()
                .exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler())
                .and()
                .exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())

                .and()
                .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
                        UsernamePasswordAuthenticationFilter.class); // JWT Token 필터를 id/password 인증 필터 이전에 추가
        return httpSecurity.build();
    }

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

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers("/v2/api-docs",
                "/swagger-resources/**",
                "/swagger-ui.html",
                "/webjars/**",
                "/swagger/**",
                "/sign-api/exception");
    }



}

두 코드의 공통점과 차이점

차이점

클래스 정의 및 상속:

-WebSecurityConfigurerAdapter-

  • WebSecurityConfigurerAdapter를 상속받아야 하며, 이를 통해 Spring Security 설정을 구성합니다.

-SecurityFilterChain-

  • 상속 없이 SecurityFilterChain 빈을 반환하는 메소드를 정의합니다. 이는 보다 모던한 접근 방식으로, Spring Framework 5.4.x 이후 권장되는 방식입니다.

메소드 시그니처:

-WebSecurityConfigurerAdapter-

  • protected void configure(HttpSecurity httpSecurity) 메소드를 오버라이딩합니다.

-SecurityFilterChain-

  • SecurityFilterChain 타입을 반환하는 configure(HttpSecurity httpSecurity) 메소드를 정의하며, @Bean 어노테이션을 사용하여 스프링 컨테이너에 빈으로 등록합니다.

반환 타입:

-WebSecurityConfigurerAdapter-

  • 반환 타입이 void이며, build() 메소드 호출이 필요하지 않습니다.

-SecurityFilterChain-

  • httpSecurity.build()를 호출하여 SecurityFilterChain 객체를 반환합니다. 이는 Spring Security 설정을 초기화하고 구성을 마무리하는 중요한 단계입니다.

공통점

  • 두 코드 모두 HttpSecurity를 사용하여 Spring Security의 세부 설정을 구성합니다. 예를 들어, CSRF 보호 비활성화, 세션 관리 정책 설정, 접근 권한 체크, 예외 처리 등의 설정이 이에 해당합니다.

  • JWT 인증 필터 추가 방식도 동일합니다. 두 방식 모두 addFilterBefore 메소드를 사용하여 JwtAuthenticationFilter를 UsernamePasswordAuthenticationFilter 클래스 이전에 추가합니다.

   .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
                        UsernamePasswordAuthenticationFilter.class); // JWT Token 필터를 id/password 인증 필터 이전에 추가

WebSecurityCustomizer란?

WebSecurityCustomizer는 Spring Security의 FilterChainProxy에 대하여 커스터마이징을 위해 사용됩니다.
WebSecurityCustomizer를 구현하면 customize() 메서드를 오버라이딩하여 필요한 보안 구성을 추가하거나 필터를 무시할 수 있습니다.

결론

스프링시큐리티에 대한 궁금증이 해결되었습니다. 트랜드에 맞춰서 권장하는 스타일대로 공부를 꾸준히 해야되겠다는 생각이 들었습니다.

profile
인생을 코딩하는 남자.

0개의 댓글