이번에는 로그인을 진행하기 위한 스프링 시큐리티에 대해 알아보는 시간을 가지려고 합니다.
다른 블로그나 자료를 참고하여 공부를 해보려고 합니다.
스프링 시큐리티(SpringSecurity)
스프링 시큐리티란?
API가 실행될 때 마다 사용자를 인증해야 하는데, 그 인증을 구현해놓은 것이 스프링 시큐리티입니다.
스프링 시큐리티는 스프링 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크입니다. 즉 인증(Authenticate, 누구인가)과 인가(Authorize, 어떤것을 할 수 있는지)를 담당하는 프레임워크입니다.
스프링 시큐리티에서는 주로 서블릿 필터(filter)와 이들로 구성된 필터체인으로의 구성된 위임 모델을 사용합니다.
그리고 보안과 관련하여 체계적으로 많은 옵션을 제공해주기 때문에 개발자 입장에서는 일일이 보안 관련 로직을 작성하지 않아도 된다는 장점이 있습니다.
기본 용어
- 접근 주체(Principal): 보호된 대상에 접근하는 유저
- 인증(Authentication): 인증은 '증명하다'라는 의미로 예를 들어, 유저 아이디와 비밀번호를 이용하여 로그인하는 과정을 말합니다.
- 인가(Authorization): '권한부여'나 '허가'와 같은 의미로 사용됩니다. 즉, 어떤 대상이 특정 목적을 실형하도록 허용(Access)하는 것을 의미합니다.
- 권한: 인증된 주체가 애플리케이션의 동작을 수행할 수 있도록 허락되었는지를 결정할 때 사용됩니다. 즉 인가 과정에서 해당 리소스에 대한 제한된 최소한의 권한을 가졌는지 확인합니다.
스프링 시큐리티 특징과 구조
- 보안과 관련하여 체계적으로 많은 옵션을 제공하여 편리하게 사용할 수 있습니다
- Filter 기반으로 동작하여 MVC와 구분하여 관리 및 동작합니다.
- 어노테이션을 통해 간단하게 설정할 수 있습니다.
- Spring Security는 기본적으로 세션 & 쿠키방식으로 인증합니다.
- 인증 관리자(Authentication Manager)와 접근 결정 관리자(Access Decision Manger)를 통해 사용자의 리소스 접근을 관리합니다.
- 인증 관리자는 UsernamePasswordAuthenticationFilter, 접근 결정 관리자는 FilterSecurityInterceptor가 수행합니다.
스프링 시큐리티 필터
클라이언트(보통 브라우저)는 요청을 보내게 되고, 그 요청을 서블릿이나 JSP등이 처리하게 됩니다. 스프링 MVC에서는 요청을 먼저 받는것이 DispatcherServlet입니다.이 DispatcherServlet이 요청 받기 전에 다양한 필터들이 있을 수 있습니다.필터가 하는 역할은 클라이언트와 자원 사이에서 요청과 응답 정보를 이용해 다양한 처리를 하는데 목적이 있습니다. 어떤 필터는 요청을 받은 후, 클라이언트가 원래 요청한 자원이 아닌 다른 자원으로 리다이렉트 시킬 수도 있습니다. 어떤 필터는 다음 필터에게 요청과 응답을 전달하지 않고, 바로 클라이언트에게 응답하고 끝낼 수 도 있습니다.
스프링 시큐리티는 다양한 기능을 가진 필터들을 10개 이상 기본적으로 제공합니다. 이렇게 제공되는 필터들을 Security Filter Chain(시큐리티 필터 체인) 이라고 말합니다.
위의 그림은 시큐리티 필터 체인과 각각의 필터에서 사용하는 객체들(Repository, Handler, Manager등)에 대해 잘 표현하고 있습니다.
좀더 단순하게 표현된 그림입니다.
각각의 기능을 살펴보겠습니다.
- SecurityContextPersistenceFilter : SecurityContextRepository에서 SecurityContext를 가져오거나 저장하는 역할을 한다.
- LogoutFilter : 설정된 로그아웃 URL로 오는 요청을 감시하며, 해당 유저를 로그아웃 처리
- (UsernamePassword)AuthenticationFilter : (아이디와 비밀번호를 사용하는 form 기반 인증) 설정된 로그인 URL로 오는 요청을 감시하며, 유저 인증 처리함
- 1. AuthenticationManager를 통한 인증 실행 2. 인증 성공 시, 얻은 Authentication 객체를SecurityContext에 저장 후 AuthenticationSuccessHandler 실행 3. 인증 실패 시, AuthenticationFailureHandler 실행
- DefaultLoginPageGeneratingFilter : 폼기반 또는 OpenID기반 인증에 사용하는 가상 URL에 대한 요청을 감시하고 로그인 폼 기능을 수행하는데 필요한 HTML을 생성함
- BasicAuthenticationFilter : HTTP 기본 인증 헤더를 감시하여 처리한다.
- RequestCacheAwareFilter : 로그인 성공 이후, 원래 인증 요청에 의해 가로채어진 사용자의 원래 요청을 재구성하는데 사용됨.
- SecurityContextHolderAwareRequestFilter : HttpServletRequestWrapper를 상속한 SecurityContextHolderAwareRequestWapper 클래스로 HttpServletRequest 정보를 감싼다. SecurityContextHolderAwareRequestWrapper 클래스는 필터 체인상의 다음 필터들에게 부가정보를 제공한다.
- AnonymousAuthenticationFilter : 이 필터가 호출되는 시점까지 사용자 정보가 인증되지 않았다면 인증토큰에 사용자가 익명 사용자로 나타난다.
- SessionManagementFilter : 인증된 주체를 바탕으로 세션 트래킹을 처리해 단일 주체와 관련한 모든 세션들이 트래킹되도록 도움
- ExceptionTranslationFilter : 이 필터는 보호된 요청을 처리하는 중에 발생할 수 있는 예외의 기본 라우팅과 위임, 전달하는 역할을 한다.
- FilterSecurityInterceptor : 이 필터는 AccessDecisionManager 로 권한부여 처리를 위임함으로써 접근 제어 결정을 쉽게해준다.
이상 다양한 필터들의 역할을 알아보았습니다.
스프링 시큐리티 인증관련 아키텍처
아이디와 암호를 입력했을 때 이를 처리하는 필터는 AuthenticationFilter입니다. 해당 필터는 다음 그림과 같은 순서로 동작합니다.
차례대로 어떻게 동작하는지 알아보겠습니다.
- 클라이언트(유저)가 로그인을 시도합니다.
- AuthenticationFilter는 AuthenticationManager, AuthenticationProvider(s), UserDetailsService를 통해 DB에서 사용자 정보를 읽어옵니다. 여기서 중요한 것은 UserDetailsService가 인터페이스라는 것입니다. 해당 인터페이스를 구현한 빈(Bean)을 생성하면 스프링 시큐리티는 해당 빈을 사용하게 됩니다. 즉, 어떤 데이터베이스로 부터 읽어들일지 스프링 시큐리티를 이용하는 개발자가 결정할 수 있게 됩니다.
- UserDetailsService는 로그인한 ID에 해당하는 정보를 DB에서 읽어들여 UserDetails 정보를 세션에 저장하게됩니다.
- 스프링 시큐리티는 인메모리 세션저장소인 SecurityContextHolder에 UserDetails정보를 저장하게 됩니다.
- 클라이언트(유저)에게 session ID(JSESSION ID)와 함께 응답을 하게 됩니다.
- 이후 요청에서는 요청 쿠키에서 JSESSION ID정보를 통해 이미 로그인 정보가 저장되어 있는지 확인합니다. 이미 저장되어 있고 유효하면 인증처리를 해주게 됩니다.
마치며
처음보는 용어가 많고, 많은 필터로 구성되어있고 알아야할 것도 많아 복잡하게 느껴지는 것 같습니다. 어떤 필터가 어떤 역할을 하고 실제로 로그인을 구현해본다면 좋을 것 같아 다음시간에 해보도록 하겠습니다.
글 및 사진 출처