HTTP 200 OK

Memento mori & Carpe diem

Spring

Spring Security 주요 아키텍처 이해( Part 1)

sjoongh 2022. 5. 30. 09:39

위임 필터 및 필터 빈 초기화

  • DelegatingProxyChain, FilterChainProxy

 

Servlet Filter 흐름

요청을 받은 WAS가 Servlet Container에게 Request, Response를 던져주면, 이를 먼저 Filter에서 받아. Request에 대한 필터링를 진행하고, 서블릿에게 ServletRequest, ServletResponse를 넘겨준다. 

요청에 대한 작업이 끝난 후 Response에 대한 필터링를 진행

쉽게말해, 요청과 응답에 대한 필터링을 진행한다.


DelegatingProxyChain

서블릿 필터에서 서블릿 필터를 구현한 스프링 빈에게 요청을 위임해주는 대리자 역할의 서블릿 필터

filter

  • 서블릿 필터는 스프링에서 정의 된 빈을 주입해서 사용할 수 없다
    • 왜냐하면 Spring Bean은 스프링 컨테이너에서 생성 및 관리하는 컴포넌트들이고, ServletFilter는 서블릿 컨테이너에서 생성 및 관리하는 필터들이기 때문에 서로 실행되는 위치가 다르기 때문이다. 하지만, 서블릿 필터와 스프링 빈 간에 호출을 하고 사용을 해야하는 경우가 생기는데, 이 때 서블릿 필터는 DelegatingFilterProxy 클래스를 사용해서 스프링 빈에게 요청을 위임하면 스프링 빈에서 구현한 서블릿 필터를 이용해 책임을 수행하게 된다.
  • 특정한 이름을 가진 스프링 빈을 찾아 그 빈에게 요청을 위임
    • springSecurityFilterChain 이름으로 생성된 빈을 ApplicationContext에서 찾아 요청을 위임
    • 실제 보안처리를 하지 않음

 

즉, 서블릿 필터에서 요청에 대한 자원의 접근 전/후로 동작을 수행하는데, 서블릿 필터는 서블릿 컨테이너에서 관리되기에 스프링 빈들을 사용할 수 없다.

 

하지만 스프링 시큐리티는 모든 요청에 대한인증 및 인가와 같은 보안 처리를 필터 기반으로 처리하고 있는데, 필터에서도 스프링의 기술(스프링 빈)을 사용하고 싶은 요구사항이 생긴다.

 

매우 예전에는 필터가 빈으로 등록되지 못했지만 현재에는 스프링에서도 스프링 빈을 만들어 서블릿 필터로 빈을 구현했는데, 이 스프링 빈이 springSecurityFilterChain이다. 하지만 위에서도 말했듯이 서블릿 필터에서는 스프링 빈들을 주입하거나 사용할 수 없는데, 이러한 필터와 빈을 연결해줄 수 있는 클래스가 DelegatingFilterProxy이다. 이 클래스는 서블릿 필터인데, 요청을 받아서 스프링에서 관리하는 필터에게 요청을 위임하는 역할을 맡고 있다.

 

서블릿 필터 예시

필터와 인터셉터 차이

Servlet Filter 란


FilterChainProxy

FilterChainProxy

  • FilterChainProxy는 각 필터들을 순서대로 호출하며 인증/인가처리 및 각종 요청에 대한 처리를 수행한다.
  • springSecurityFilterChain의 이름으로 생성되는 필터 빈
  • DelegatingFilterProxy로 부터 요청을 위임 받고 실제 보안 처리
  • 스프링 시큐리티 초기화 시 생성되는 필터들을 관리하고 제어
    • 스프링 시큐리티가 기본적으로 생성하는 필터
    • 설정 클래스에 API추가 시 싱성되는 필터
  • 사용자의 요청을 필터 순서대로 호출하여 전달
  • 사용자정의 필터를 생성해서 기존의 필터 전,후로 추가 가능
    • 필터의 순서를 잘 정의
    • 마지막 필터까지 인증 및 인가 예외가 발생하지 않으면 보안 통과

Flow

서블릿 컨테이너에서 스프링 컨테이너로 DelegatingFilterProxy 필터를 이용해 요청을 위임하는 흐름은 다음과 같다.

Flow

  1. 사용자가 자원 요청
  2. servlet Container의 필터들이 처리를 하게되고 그 중 DelegatingFilterProxy가 요청을 받게 될 경우 자신이 요청받은 요청객체를 delegate request로 요청 위임을 한다.
  3. 요청 객체는 특별한 필터(springSecurityFilterChain)에서 받게 된다.
    • DlelgatingFilterProxy가 필터로 등록 될 때 springSecurityFilterChain 동일한 이름으로 등록하여 내부적으로 해당 이름을 가진 객체를 찾는 것
    • springSecurityFilterChain 필터를 가지고 있는 빈(Bean) FilterChainProxy이다.
  4. FilterChainProxy에서는 자신이 가진 각각의 필터들을 차례대로 수행하며 보안처리를 수행한다.
  5. 보안처리가 완료되면 최종 자원에 요청을 전달하여 다음 로직이 수행된다.

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

스프링 시큐리티에서는 보안 설정을 단일 설정이 아닌 여러 개의 설정을 만들어서 동시에 사용을 할 수 있다.

security

  • 설정클래스 별로 보안 기능이 각각 작동
  • 설정 클래스 별로 RequestMatcher 설정 -> http.antmatcher("/admin/**")
  • FilterChainProxy가 각 필터들을 가지고 있음
  • 요청에 따라 RequestMatcher와 매칭되는 필터가 작동하도록 함

FilterChainProxy

  1. GET방식으로 /admin 주소로 자원 요청
  2. FilterChainProxy에서 요청을 받아 요청을 처리할 필터를 선택
  3. 요청 URL과 matches를 하여 true가 되는 Filter를 선택해야 한다.
    • FilterChainProxy가 저장하고 있는 각각의 SecurityConfig 객체들에서 RequestMacher의 정보와 매치되는 정보를 찾는다.
  4. 일치하는 객체의 필터를 수행하여 인증/인가 처리를 한다.
  • @Order(0) : 여러 개의 securityConfig를 작성하고 싶다면 @Order annotation을 사용하여 순서를 지정할 수 있다.
    • order의 숫자가 낮은 것부터 먼저 실행됨
    • 넓은 범위부터 검사해 통과할 경우 좁은 범위의 접근권한을 검사하지 않기 때문에 주의해야 한다.

Sequence Diagram

Diagram


인증 개념 이해


Authentication 인증객체란?

  • 당신이 누구인지 증명하는 것
    • 사용자의 인증 정보를 저장하는 토큰 개념으로 사용한다.
    • 인증 시 id와 password를 담고 있는 인증 검증을 위해 전달되어 사용된다
    • 인증 후 최종 인증 결과(user 객체, 권한 정보)를 담고 SecurityContext에 저장되어 전역적으로 참조가 가능하다.
Authentication authentication = 
    SecurityContexHolder.getContext().getAuthentication();
  • Authentication 객체의 구조
    1. principal : 사용자 아이디 혹은 User 객체를 저장
    2. credentials : 사용자 비밀번호
    3. authorities : 인증된 사용자의 권한 목록
    4. details : 인증 부가 정보
    5. Authenticated : 인증 여부(Bool)

Flow

Authentication이라는 인증 객체가 클라이언트 요청시 사용이 되고, 인증이 끝나고 활용되는지에 대한 그림

Flow

  1. 사용자가 로그인을 시도(username + password 입력 및 전달)
  2. usernamePasswordAuthenticationFolter(인증필터)가 요청정보를 받아서 정보 추출을 하여 인증객체(Authentication)을 생성한다.
  3. AuthenticationManager가 인증객체를 가지고 인증처리를 수행한다.
    • 인증이 실패하게 되면 예외 발생
  4. 인증 성공후 Authentication 인증객체를 만들어서 내부의 Principal, Credentials, Authorities, Authenticated 들을 채워 넣는다.
  5. SecurityContextHolder 객체 안의 SecurityContext에 저장한다.
    • 인증 객체를 전역적으로 사용할 수 있게 된다.

서버에서 사용자 별 Authentication 인증 객체를 어떻게 구분하는가?

위 그림에서도 나와있듯이 SecurityContextHolder라는 전역 객체 안에 SecurityContext에 인증 객체를 저장하는데, 이 SecurityContextHolder는 ThreadLocal에 저장되기 때문에 각기 다른 쓰레드별로 다른 SecurityContextHolder 인스턴스를 가지고 있어서 사용자 별로 각기 다른 인증 객체를 가질 수 있다.