Spring Security란 인증과, 권한부여, 일반적인 공격에 대한 보호의 기능을 제공하는 프레임워크이다
스프링 시큐리티는 막강한 인증과 인가 기능을 가진 프레임 워크
스프링 기반의 애플리케이션에서 보안을 위한 표준
인터셉터, 필터 기반의 보안 기능을 구현하는 것 보다 스프링 시큐리티를 통해 구현하는 것이 선호됨
Spring Security를 사용하면 어플리케이션의 보안 관련 기능을 자체적으로 구현 할 필요 없이 쉽고 안전하게 구현할 수 있다.
Spring Security의 Servlet 보안 지원은 Servlet Filter(이하 Filter)를 기반으로 한다. 클라이언트가 어플리케이션으로 request를 보내면,
Servlet Container는 요청 URI의 경로에 따라 어떤 Filter와 어떤 Servlet을 적용할 것인지 결정한다.
servlet이란? 클라이언트의 Request에 대해 동적으로 작동하는 웹 어플리케이션 컴포넌트이다
예를들어, 로그인 시도를 할 때, 서버가 클라이언트에서 입력되는 아이디와 비밀번호를 확인하고 결과를 응답하는데 이러한 역할을 수행하는 것이 서블릿이다
Spring은 여러 Filter중 Servlet Container Lifecycle과 ApplicationContext사이에 연결할 수 있는 DelegatingFilterProxy라는 Filter를 제공한다.
Spring Security의 Servlet Filter 지원은 DelegatingFilterProxy가 감싸고 있는 FilterChainProxy에 의해 수행되며, FilterChainproxy는 Security Filter Chain을 통해 많은 작업을 Security Filter 인스턴스에 위임한다.
위의 설명을 그림으로 나타내면 아래와 같다
Security Filter Chain은 스프링에서 보안과 관련된 여러 Security Filter List를 갖고 있는 객체로 이를 순회하면서 필터링을 실시한다.
SecurityFilterChain에 존재하는 Security Filter순서는 다음과 같다.
위의 Security Filter중 Security Authentication Filter(UsernamePasswordAuthentication, OAuth2LoginAuthenticationFilter와 같은 인증 필터)는 AuthenticationManager를 통해 인증을 수행한다.
AuthenticationManager는 Spring Security의 필터들이 인증을 수행하는 방법에 대한 명세를 정의해 놓은 인터페이스이다.
이 AuthenticationManager는 일반적으로 ProviderManager로 구현되며, ProviderManager는 여러 AuthenticationProvider에 인증을 위임한다.
여러 AuthenticationProvider중 하나라도 인증에 성공한다면 ProviderManager에게 인증된 Authentication객체를 반환하고 이는 event 기반으로 AuthenticationFilter에 전송된다.
ProviderManager에 설정된 AuthenticationProvider중 어느 것도 성공적으로 인증을 수행할 수 없다면, 인증은 실패할 것이고 알맞는 예외가 ProviderManager에게 건내질 것이다.
인증이 성공할 경우 AuthenticationFilter는 SecuritycontextHolder의 SecurityContext에 인증된 Authentication 객체를 저장할 수 있도록 한다.
아래 그림은 Username and Password 인증 방식의 아키텍처이다
위 그림의 AuthenticationFilter의 역할은 UsernamePasswordAuthenticationFilter가 수행한다.
정리하자면 전체적인 프로세스는 다음과 같다.
사용자가 로그인 정보와 함께 인증 요청 (HttpRequest
)하면, Servlet Filter에 의해서 Security Filter로 Security 작업이 위임되고 여러 Security Filter 중에서 UsernamePasswordAuthenticationFilter
(Username and Password Authentication 방식에서 사용하는 AuthenticationFilter)에서 인증을 처리한다.
AuthenticationFilter
(UsernamePasswordAuthenticationFilter인데 지금부터 AuthenticationFilter라고 부름)는 Servlet 요청 객체(HttpServletRequest)에서 username과 password를 추출해 UsernameAuthenticationToken
(이하 인증 객체)을 생성한다.
AuthenticationFilter
는 AuthenticationManager
(구현체 : ProviderManager)에게 인증 객체 UsernamePasswordAuthenticationToken
객체를 전달
ProviderManager
는 인증을 위해 AuthenticationProvider
에게 인증 객체를 전달한다.
AuthenticationProvider
는 전달받은 인증 객체의 정보(일반적으로 사용자 아이디)를 실제 DB로 부터 사용자 인증 정보를 가져오는 UserDetailsService
에 사용자 정보를 넘겨줌
UserDetailsService
는 전달 받은 사용자 정보를 통해 DB에서 알맞는 사용자를 찾고 이를 기반으로 UserDetails
객체를 만든다.
사용자 정보와 일치하는 UserDetails
객체를 AuthenticationProvider
에 전달한다
AuthenticationProvider
은 전달받은 UserDetails
를 인증해 성공하면 ProviderManager
에게 권한(Authorities)을 담은 검증된 인증 객체를 전달한다.
ProviderManager
는 검증된 인증 객체를 AuthenticationFilter
에게 전달한다. (event 기반 으로 전달)
AuthenticationFilter
는 검증된 인증 객체를 SecurityContextHolder
의 SecurityContext
에 저장한다.
주의 깊게 살펴봐야할 부분은 UserDetailsService
와 UserDetails
이다.
실질적인 인증 과정은 사용자가 입력한 데이터 (ID, PW 등) 와 UserDetailsService
의 loadUserByUsername()
메서드가 반환하는 UserDetails
객체를 비교함으로써 동작한다
따라서 UserDetailsService
와 UserDetails
구현을 어떻게 하느냐에 따라서 인증의 세부 과정이 달라진다.
*추가 2023.05.20
아래는 스프링 시큐리티에서 동작하는 기본 필터들의 목록 및 순서이다.
만약 OAuth2 2.0 로그인을 사용한다면 UsernamePasswordAuthenticationFilter
대신에 OAuth2LoginAuthenticationFilter
가 호출된다
스프링 시큐리티는 AbstractAuthenticationProcessingFilter
를 호출하고, 로그인 방식에 따라 구현체인 UsernamePasswordAuthenticationFilter
와 OAuth2LoginAuthenticationFilter
가 동작하는 방식이다.
https://imbf.github.io/spring/2020/06/29/Spring-Security-with-JWT.html
https://gngsn.tistory.com/160
https://velog.io/@tmdgh0221/Spring-Security-%EC%99%80-OAuth-2.0-%EC%99%80-JWT-%EC%9D%98-%EC%BD%9C%EB%9D%BC%EB%B3%B4#oauth-20