Spring Security Filter

 

Spring Security는 서블릿 필터 체인을 통한 위임 모델을 구현한다.

가 무슨 뜻인지 알아보자.

 

 

서블릿 필터

 

 

Servlet Filter

 

서블릿 필터는 사용자의 request 또는 서버의 response 를 중간에서 가로채서 필터링하는 인터페이스

init(), doFilter(), destory() 메서드가 있다.

 

public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {
    }

    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    default void destroy() {
    }
}

 

 

 

이러한 필터를 순차적으로 여러개 수행하는 것을 필터 체인이라고 한다.

 

Filter Chain

 

 

시큐리티는 여러 필터를 가진 서블릿 필터 체인을 자동으로 구성하고 필터링을 위임하여 순차적으로 실행한다.

몇 가지 주요 필터를 알아보자.

 

 

Spring Security 주요 필터

 

- SecurityContextPersistenceFilter

SecurityContextRepository를 통해 SecurityContext를 load, save 처리한다.

 

- LogoutFilter

로그아웃 URL(디폴트 값: /logout) 로의 요청을 감시하여 해당 사용자를 로그아웃 처리한다.

 

- UsernamePasswordAuthenticationFilter

ID/PW 기반 Form 인증 요청 URL(디폴트 값: /login) 을 감시하여 사용자를 인증한다.

 

- ExceptionTranslationFilter

요청을 처리하는 중에 발생할 수 있는 예외를 위임하거나 전달한다.

 

- FilterSecurityInterceptor

접근 권한 확인을 위해 요청을 AccessDecisionManager로 위임한다.

(이 필터가 실행되는 시점에는 사용자가 인증되었다고 판단한다.)

 

 

 

 

 

Spring Security 인증 처리

 

Spring Security 에는 아래와 같이 인증 절차가 구현되어 있다.

인증 절차를 이해하고 나면 구현체와 설정을 통해 인증 절차를 커스터마이징 할 수 있다.

 

 

--> Spring Security는 AuthenticationManager(ProviderManager)가 가지고 있는 provider 목록을 순회하면서

provider 가 실행 가능한 경우에 provider의 authenticate 메서드를 호출하여 인증 절차를 수행한다.

 

 

 

 

인증 처리 핵심 컴포넌트

 

- UsernamePasswordAuthenticationFilter (디폴트 인증 필터)

  • 사용자 인증 요청을 Authentication 인터페이스로 추상화하고, AuthenticationManager 를 호출한다.

 

- AuthenticationManager

  • 사용자 ID/PW 를 인증하기 위해 적절한 AuthenticaitonProvider 를 찾아 처리를 위임한다.
  • AuthenticationFilter 에 의해 AuthenticationManager가 동작한다.

 

- ProviderManager

  • ProviderManager 는 AuthenticationManager 의 가장 일반적인 구현체이다.
  • ProviderManager 는 AuthenticationProvider 목록을 위임받는다.
  • 각 AuthenticationProvider 는 인증 성공, 실패, 결정할 수 없음을 나타낼 수 있고, 나머지 AuthenticationProvider 가 결정할 수 있도록 전달한다.

 

- AuthenticationProvider

  • 실질적으로 사용자 인증을 처리하고, 인증 결과를 Authentication 인터페이스로 반환한다.
  • 실제 인증 로직이 담긴 객체

 

 

 

WebSecurityConfigure

 

시큐리티 버전이 업데이트 됨에 따라 websecurityconfigureradapter 가 deprecated 되어서

websecurityconfigureradapter 를 상속하지 않고 설정파일을 작성해보았다.

 

 

@Configuration
@EnableWebSecurity // SpringSecurityFilterChain 포함
public class WebSecurityConfigure {

    ...
    ...

	
    // @Bean
    // public WebSecurityCustomizer webSecurityCustomizer() {
    //    return (web) -> web.ignoring().antMatchers("/swagger-resources", "/webjars/**", "/static/**", "/templates/**", "/v2/**", "/h2/**");
    // }
    // -------> resources 메서드로 변경
    
    @Bean
    @Order(0)
    public SecurityFilterChain resources(HttpSecurity http) throws Exception {
        return http.requestMatchers(matchers -> matchers
                        .antMatchers("/swagger-resources", "/webjars/**", "/static/**", "/templates/**", "/v2/**", "/h2/**"))
                .authorizeHttpRequests(authorize -> authorize
                        .anyRequest().permitAll())
                .build();
    }

    // configureAuthentication 메서드는 스프링 부트 2.6.X 부터 순환참조 오류 발생해서
    // 다른 config 클래스로 옮기든지 application.yml 설정 변경해줘야함
    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder builder, JwtAuthenticationProvider authenticationProvider) {
        // AuthenticationManager 는 AuthenticationProvider 목록을 지니고 있다.
        // 이 목록에 커스텀 Authentication Provider 추가
        builder.authenticationProvider(authenticationProvider);
    }

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

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

    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter(jwtTokenConfigure.getHeader(), jwt);
    }


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf()
                .disable()
                .headers()
                .disable()
                .and()
                .sessionManagement()
                // JWT 인증을 사용하므로 무상태(STATELESS) 전략 설정
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .antMatchers("/api/_hcheck").permitAll()
                .antMatchers("/api/**").hasRole(Role.USER.name())
                .anyRequest().permitAll()
                .and()
                // JWT 인증을 사용하므로 form 로그인은 비활성화
                .formLogin()
                .disable();
        http
                // 커스텀 필터 추가
                // UsernamePasswordAuthenticationFilter 앞에 jwtAuthenticationTokenFilter 를 추가한다.
                .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

 

 

 

 

 

참고

https://gregor77.github.io/2021/05/18/spring-security-03/

https://springsource.tistory.com/80

https://cjw-awdsd.tistory.com/45

복사했습니다!