[ 정수원 스프링 시큐리티 #2 ] 스프링 시큐리티 주요 아키텍처 이해 (1)

김수호·2024년 3월 11일
0
post-thumbnail

이번 섹션에서는 [스프링 시큐리티 주요 아키텍처 이해]에 대해서 알아보자.

👉 목차는 다음과 같다.

1) 위임 필터 및 필터 빈 초기화 - DelegatingProxyChain, FilterChainProxy
2) 필터 초기화와 다중 보안 설정

3) 인증 개념 이해 - Authentication
4) 인증 저장소 - SecurityContextHolder, SecurityContext
5) 인증 저장소 필터 - SecurityContextPersistenceFilter
6) 인증 흐름 이해 - Authentication Flow
7) 인증 관리자 : AuthenticationManager
8) 인증 처리자 : AuthenticationProvider
9) 인가 개념 및 필터 이해 : Authorization, FilterSecurityInterceptor
10) 인가 결정 심의자 : AccessDecisionManager, AccessDecisionVoter
11) 스프링 시큐리티 필터 및 아키텍처 정리

이번 섹션은 1) ~ 2), 3) ~ 5), 6) ~ 8), 9) ~ 10), 11) 로 나눠서 포스팅하고자 한다.

바로 하나씩 확인해보자.


1) 위임 필터 및 필터 빈 초기화 - DelegatingProxyChain, FilterChainProxy

이번 내용에서는 DelegatingFilterProxy, FilterChainProxy 두 클래스에 대해서 알아보자.

✔️ DelegatingFilterProxy

  • 참고)
    • 서블릿 필터는 스프링에서 정의된 빈을 주입해서 사용할수 없다.
    • 따라서 특정한 이름을 가진 스프링 빈을 찾아 그 빈에게 요청을 위임한다. 그 역할을 DelegatingFilterProxy 가 한다.
      • springSecurityFilterChain 라는 이름으로 생성된 빈을 ApplicationContext 에서 찾아 요청을 위임한다.
      • 이 DelegatingFilterProxy 클래스에서는 실제 보안처리를 하지는 않는다. 요청을 위임하는 역할만 한다.

그러면 springSecurityFilterChain 라는 이름을 가진 빈이 어떤 빈인지에 대해서 알아보자.

✔️ FilterChainProxy

  • ① springSecurityFilterChain 의 이름으로 생성되는 필터 빈 (참고. WebSecurity)
    • FilterChainProxy 는 서블릿 필터를 구현한 필터 타입이며, 스프링 시큐리티가 초기화 될 때 스프링 빈으로 생성된다.
  • ② DelegatingFilterProxy 으로 부터 요청을 위임 받고 실제 보안 처리를 하는 필터 클래스이다.
  • ③ 스프링 시큐리티 초기화 시 생성되는 필터들을 관리하고 제어한다. (아래 이미지 참고)
    • 스프링 시큐리티 초기화 시 생성되는 필터는 다음과 같다.
    • 1) 스프링 시큐리티가 기본적으로 생성하는 필터
    • 2) 설정 클래스에서 API 추가 시 생성되는 필터
  • ④ 사용자의 요청을 필터 순서대로 호출하여 전달
  • ⑤ 사용자정의 필터를 생성해서 기존의 필터 전,후로 추가 기능
    • 필터의 순서를 잘 정의
  • ⑥ 마지막 필터까지 인증 및 인가 예외가 발생하지 않으면 보안 통과
  • 참고)
    • FilterChainProxy 는 체인으로 연결된 각각의 필터들을 순서대로 호출하면서 사용자의 요청에 대한 인증/인가 처리를 진행한다.

 

👉 앞서 살펴본 DelegatingFilterProxy 와 FilterChainProxy의 전체적인 흐름을 다시 살펴보자.

  • 참고)
    • 사용자가 요청을 하게되면 요청이 서블릿 자원으로 가기 전에 서블릿 컨테이너의 여러 필터들이 요청에 대한 처리를 진행하게 된다.
    • 그 필터들 중에서 DelegatingFilterProxy 가 있는데, 이 클래스는 특정한 이름(springSecurityFilterChain)을 가진 빈을 찾아서 그 빈에게 요청을 위임한다. ( 그 빈이 FilterChainProxy 이다. )
    • FilterChainProxy 는 위임 받은 요청에 대해서, 자신이 관리하고 있는 각각의 필터를 호출하면서 요청에 대한 보안 처리를 진행한다. 보안 처리가 완료되면, 다시 서블릿 컨테이너의 나머지 필터들로 전달되고, 모든 필터가 통과되면 Spring MVC DispatcherServlet 에 요청을 전달해서 요청에 대한 처리를 진행한다.

 

✔️ 참고


2) 필터 초기화와 다중 보안 설정

이번 내용에서는 필터 초기화와 다중 보안 설정에 대해서 알아보자.

  • 참고)
    • (참고) 그림을 보면 직접 구현한 2개의 보안 설정 클래스(SecurityConfig1, SecurityConfig2) 가 있는 것을 확인할 수 있다.
      • 각 설정 클래스에서는 WebSecurityConfigurerAdapter 클래스를 상속받고, HttpSecurity를 통해서 인증/인가와 관련된 API를 설정했다. 그렇게 되면 스프링 시큐리티가 초기화되면서 우리가 설정한 API와 관련된 필터들을 생성한다.
    • 설정클래스 별로 보안 기능이 각각 작동한다.
    • 설정클래스 별로 RequestMatcher를 설정할 수 있다.
      • ex) http.antMatcher("/admin/**") , http.antMatcher("/user/**")
    • 설정클래스 별로 필터가 생성된다.
    • FilterChainProxy가 각 필터들을 가지고 있다.
    • 사용자 요청시, 요청에 따라 RequestMatcher와 매칭되는 필터가 작동한다.

 

요청 흐름을 좀 더 자세히 살펴보자.

  • 참고)
    • 사용자가 GET 방식으로 /admin 자원 경로에 접근한다.
    • 그러면 FilterChainProxy가 그 요청을 받아 그 요청을 처리할 필터를 선택한다.
      • 이때, SecurityFilterChain 을 순회하면서 사용자의 요청 경로과 RequestMatcher 가 매칭 되는지 체크하고, 매칭되는 경우 해당하는 필터를 가져온다.
      • 따라서 위의 경우, SecurityConfig1에서 정의된 필터를 가져와서 인증과 인가처리를 진행한다.
      • 결과적으로, 다중 설정 클래스를 여러개 만들더라도 RequestMatcher만 다르게 구분하면, 사용자의 요청과 RequestMatcher를 매칭하여, 해당하는 필터들을 가져와서 처리한다.

 

👉 코드로 작성하여 확인해보자.

  • SecurityConfig 수정
    • 두 개의 설정 클래스를 생성했다.
      • /admin 하위 경로로 접근시 첫 번째 설정클래스의 보안 기능이 작동하도록 RequestMatcher가 설정되었고, 두 번째 설정클래스는 첫 번째 처럼 RequestMatcher에 개별적으로 경로를 지정하지 않았다. 따라서 모든 요청에 해당 보안 설정 클래스가 작동하도록 설정했다.
  • 서버를 기동해보자.
    • 스크링 시큐리티는 설정 클래스가 여러개일 때 순서에 따라서 초기화한다. 그리고 이 순서는 우리가 @Order를 통해 지정해주어야 한다. ( 현재 같은 순서이기 때문에 오류가 발생한다. )
  • @Order 애노테이션을 설정 후 다시 기동해보자.
    • 이제 정상적으로 기동되는 것을 확인할 수 있다.
    • 주의) 여기서 @Order 의 순서가 중요하다.
      • FilterChainProxy 가 사용자의 요청을 처리할 필터를 가져오기 위해 SecurityFilterChain 를 순회할 때, 우리가 설정한 순서대로 비교를 한다.
      • 따라서, SecurityConfig 와 SecurityConfig2 의 순서를 변경하면, 사용자가 /admin 하위 경로로 접근하더라도, SecurityConfig2 기준으로 보안이 적용되어, 인증을 받지 않고도 접근이 가능해진다. 그러므로 다중 보안 설정 클래스로 서비스를 운영할 경우, 구체적인 범위의 요청을 더 앞 순서로 해서 @Order를 설정해야 하는 것에 주의하자.
  • WebSecurity를 디버깅해보자.
    • 참고) WebSecurity 라는 클래스에서 FilterChainProxy 를 생성한다. 그리고 FilterChainProxy 를 만들 때, 생성자에 securityFilterChains 리스트를 넘겨주고 있다.
    • securityFilterChains 에는 우리가 설정클래스에서 작성한 정보를 담고있는 두개의 객체(SecurityFilterChain)가 담겨있다. 그리고 각 객체에는 requestMatcher 변수와 filters 라는 리스트 타입의 변수가 있다.
      • 첫 번째 객체는 RequestMatcher로 /admin/** 가 담겨져 있고, 12개의 필터가 있다.
      • 두 번째 객체는 RequestMatcher로 any request 가 담겨져 있고, 14개의 필터가 있는 것을 확인할 수 있다.
  • 루트 경로로 요청해보자.
    • 첫 번째 이미지: 사용자의 요청을 받은 FilterChainProxy 를 확인해보자. 현재 두개의 SecurityFilterChain 을 가지고 있는 filterChains 를 확인할 수 있다. 이제 여기서 사용자의 요청을 처리할 필터를 조회(getFilters)한다.
    • 두 번째, 세 번째 이미지: 사용자의 요청을 처리할 필터를 가져올 때, 사용자가 요청한 경로와 filterChains 의 각 SecurityFilterChain 이 가지고 있는 RequestMatcher 와 매칭하여, 매칭되는 객체에 담겨있는 필터들을 가져온다. 여기서는 사용자가 루트 경로로 접근했으므로, 두 번째 설정 클래스에 해당하는 필터들을 가져온다. ( 이후 가져온 각 필터들을 차례대로 호출하면서 사용자의 요청을 처리한다. )

 

✔️ 참고

 

이번 내용에서는 다중 설정 클래스를 만들었을 때 스프링 시큐리티가 초기화되면서 각각의 설정 클래스별로 어떻게 필터들을 관리하고, 또 사용자의 요청에 따라서 어떤식으로 해당하는 설정 클래스의 보안 기능이 작동되도록 하는지 살펴보았다.


강의를 듣고 정리한 글입니다. 코드와 그림 등의 출처는 정수원 강사님께 있습니다.

profile
현실에서 한 발자국

0개의 댓글