Web Dev/Spring

Spring Security - Filter의 동작 원리

DuL2 2022. 8. 2. 18:06

Spring Security 동작 원리 이해하기

 이 글은 다른 글들을 짜집기하여 본인의 Spring Security 이해를 돕기 위해 작성하는 글입니다. Security를 커스터마이징하여 사용하기 앞서 동작 원리를 이해하고 프로젝트에 적용하기 위해 정리를 합니다.

 

 Spring Security는 웹 서비스에서 MVC 패턴 이전에 작동하는 `필터`로서 동작합니다. 필터란 클라이언트 요청이 서블릿으로 가기 전에 먼저 처리할 수 있도록 톰캣(WAS)에서 지원해주는 기능입니다. 그래서 설정도 톰캣의 설정파일인 `web.xml`에 하지만, 스프링 부트를 사용하는 환경이고, 최신 스프링 부트(2.7 버전 이상)에서는 컴포넌트 기반의 설정을 지향하므로 컴포넌트 기반의 설정법을 다루어볼 예정입니다.

 

 이 글의 중심은 Spring Security가 어떻게 필터 역할을 하는지에 대해서 중점적으로 다루어보려고 합니다.

스프링 시큐리티의 동작!

          1. 톰캣(WAS)가 구동되면서 실행할 필터들의 정보를 수집.
            • 필터의 가장 큰 역할은 사용자 요청을 검증하고 필요에 따라 데이터를 추가하거나 변조하는 것이라고 보면 됩니다. 톰캣은 기본적으로 encoding 필터를 가지며 UTF-8 타입으로 변경해줍니다.
            • 스프링 시큐리티 인터셉터는 요청에 대해서 특정 권한이 없을 경우 로그인 URL로 변경하여 서블릿에게 전달해 줍니다. 실제 request 객체의 요청을 변경하는 과정을 통해서 애플리케이션이 원하는 방향으로 유도하는 것입니다.
          2. 톰캣(WAS)에서 필터 클래스의 객체 생성 후 doFilter() 메소드 호출
            • 각 filter는 doFilter() 메소드를 가지고 있고 request,와 response, filterchain을 파라미터로 가지며 filter의 역할을 다하게 되면 다음 filter로 넘겨주는 역할을 합니다.
          3. `DelegatingFilterProxy`가 Security filter를 등록.
               
            •  
            • 제가 이해한 바를 설명하자면 톰캣(WAS)는 필터를 가지고 있고 위 그림처럼 FilterChain을 통해(web.xml에 정의된) 일련의 필터링 과정을 거치게 됩니다. 
            • 이 때 Spring이 제공하는 `DelegatingFilterProxy`가 서블릿 컨테이너의 생명주기와 스프링의 컨테이너 즉 ApplicationContext와의 접점으로 존재하게 되는데,
            • `DelegatingFilterProxy`는 우리가 Spring Security Config에 설정해두었던 filter 값들을 FilterChain에 탑재시키면서 클라이언트에서 서버로 접근해오는 요청들을 필터 처리할 수 있도록 설정해줍니다.
            • 각 filter는 각자 본연의 로직을 처리하고 위에서 언급했던 filterChain.doFilter()를 통해 다음 filter 가 있는지 식별하고 마지막 filter라면 서블릿으로 요청을 전달하여 최종적으로 MVC로 보내게 됩니다. `DelegatingFilterProxy`의 역할 - 스프링 Security에 정의한 filter들을 filter chain에 올려줌.
            • spring.io 공식 문서
          4. FilterChain에 등록되는 Security Filter는 정해진 순서대로 동작.
            • 다음은 공식 문서에 제공되는 표준 filter와 작동되는 순서입니다.
            • 공식 문서
          5. 필터 클래스는 각 모듈 별 기능을 가진 객체(클래스)의 메소드를 호출해서 로직을 전개
            • 아직 디테일하게 이해하지는 못했지만 각 필터의 로직이 전개되면서 구현체에서 필요한 로직을 가져다가 사용한다는 뜻 같습니다.
            • 예를들어 RememberMeService 구현체에는 autoLogin(), loginFail(), loginSuccess(), logout() 등의 메소드들이 있지만 이 메소드 들을 호출하는 필터는 다 다르다는 이야기 입니다.
            • 각각 autoLogin(), loginFail() 는 RememberMeAuthenticationFilter가 자동 로그인 기능과 권한 등록 과정에서 예외 발생시 사용하고, loginSuccess()는 UsernamePasswordAuthenticationFilter가 리멤버-미를 체크하고 로그인 했을 경우 쿠키 발행 및 등록을 처리할 경우 사용하고, logout() 은 LogoutFilter가 로그아웃시 쿠키 및 DB정보를 삭제할 때 사용하게 됩니다.

https://webfirewood.tistory.com/m/115?category=694472

추가로 공식 문서에는 `after`와 `before`라는 속성으로 filter 사이에 insert 할 수 있다고 합니다.

이를 통해서 커스터마이징한 필터의 순서를 정할 수 있습니다. 다음 코드는 UsernamePasswordAuthenticationFilter 가 동작전에 filter를 위치시키는 코드입니다.

http
        .addFilterBefore(formLoginFilter(), UsernamePasswordAuthenticationFilter.class)
        .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);