이 시리즈는 Spring Security에 대한 학습을 한 후 기록하는 시리즈입니다. 해당 시리즈는 Spring Security 5.7 버전이므로 현재 6.x 버전과 설정방법은 다르지만 개념적 이해는 같으므로 5.7로 진행했습니다.
springSecurityFilterChain
의 이름으로 생성되는 필터 빈입니다DelegatingFilterProxy
로부터 요청을 위임받고 실제 보안 처리를 합니다.RequestMatcher
설정합니다http.antMatcher("/admin/**")
FilterChainProxy
가 각 필터들을 가지고 있습니다RequestMatcher
와 매칭되는 필터가 작동하도록 합니다SecurityContext
에 저장되어 전역적으로 참조가 가능합니다Authentication authentication = SecurityContextHolder.getContext().getAuthentication()
User
객체를 저장Authentication
객체가 저장되는 보관소로 필요시 언제든지 Authentication
객체를 꺼내어 쓸 수 있도록 제공되는 클래스입니다ThreadLocal
에 저장되어 아무 곳에서나 참조가 가능하도록 설계되어 있습니다HttpSession
에 저장되어 어플리케이션 전반에 걸쳐 전역적인 참조가 가능합니다SecurityContext
객체 저장 방식MODE_THREADLOCAL
: 스레드당 SecurityContext
객체를 할당, 기본값MODE_INHERITABLETHREADLOCAL
: 메인 스레드와 자식 스레드에 관하여 동일한 SecurityContext
를 유지MODE_GLOBAL
: 응용 프로그램에서 단 하나의 SecurityContext
를 저장SecurityContextHolder.clearContext()
: SecurityContext
기존 정보 초기화SecurityContext
객체를 생성하여 SecurityContextHolder
에 저장합니다AnonymousAuthenticationFilter
에서 AnonymousAuthenticationToken
객체를 SecurityContext
에 저장합니다SecurityContext
객체를 생성하여 SecurityContextHolder
에 저장합니다UsernamePasswordAuthenticationFilter
에서 인증 성공 후 SecurityContext
에 UsernamePasswordAuthentication
객체를 SecurityContext
에 저장합니다Session
에 SecurityContext
를 저장합니다Session
에서 SecurityContext
를 꺼내어 SecurityContextHolder
에서 저장합니다SecurityContext
안에 Authentication
객체가 존재하면 계속 인증을 유지합니다SecurityContextHolder.clearContext()
AuthenticationProvider
목록 중에서 인증 처리 요건에 맞는 AuthenticationProvider
를 찾아서 인증 처리를 위임합니다ProviderManager
를 설정하여 AuthenticationProvider
를 계속 탐색할 수 있습니다AuthenticatonProvider
는 인터페이스이고, 주로 어플리케이션에 맞게 커스텀하게 구현해서 사용하는 경우가 많습니다authenticate
: 인증을 위해서 검증하는 메소드supports
: 인증을 처리할 수 있는 기준이 되는지 확인하는 메소드AuthenticationException
을 발생시킵니다AccessDeniedException
을 발생시킵니다AccessDecisionManager
에게 위임합니다Voter
들을 가질 수 있으며, Voter
들로부터 접근허용, 거부, 보류에 해당하는 각각의 값을 리턴받고 판단 및 결정을 합니다Voter
클래스 중 하나라도 접근 허가로 결론을 내면 접근 허가로 판단합니다allowIfEqualGrantedDeniedDecisions
를 false
로 설정하면 접근 거부로 결정됩니다Voter
가 만장일치로 접근을 승인해야 하며 그렇지 않은 경우 접근을 거부합니다판단을 심사하는 위원의 역할을 수행합니다
Voter
가 권한 부여 과정에서 판단하는 자료
Authentication
: 인증 정보(user)FilterInvocation
: 요청 정보(antMatcher("/user"))ConfigAttributes
: 권한 정보(hasRole("USER"))결정 방식
ACCESS_GRANTED
: 접근 허용(1)ACCESS_DENIED
: 접근 거부(0)ACCESS_ABSTAIN
: 접근 보류(-1)Voter
가 해당 타입의 요청에 대해 결정을 내릴 수 없는 경우Config
에 설정한 내용을 토대로 사용할 HttpSecurity
가 Filter
들을 구성합니다(Security 초기화)WebSecurity
가 HttpSecurity
가 설정한 filter
들의 정보를 전달받은 후 FilterChainProxy
의 빈 객체(springSecurityFilterChain
)를 생성할 때, 해당 filter
들을 인자로 넘겨서 생성합니다DelegatingFilterProxy
는 특정 이름(springSecurityFilterChain)의 빈을 찾게 됩니다. DelegatingFilterProxy
는 사용자의 요청을 찾은 springSecurityFilterChain
에 위임합니다DelegatingFilterProxy
가 사용자의 인증 요청을 받고, FilterChainProxy
에 위임합니다FilterChainProxy
는 이미 초기화 과정에서 관련 filter
들의 목록을 가지고 있는 상태이므로, 위임을 받으면 해당 필터들을 순서대로 호출합니다.SecurityContextPersistenceFilter
가 사용자의 요청을 받고, 내부에 있는 HttpSessionSecurityContextRepository
가 로직을 수행합니다. 우선 loadContext
를 실행해서 이전에 생성된 context
가 있는지 확인해 보고 없다면 새로운 context
를 생성하고, 다음 필터로 넘어갑니다 LogoutFilter
는 로그아웃 요청이 없다면 특별히 기능을 수행하지 않고 다음 필터로 넘어갑니다UsernamePasswordAuthenticationFitler
) 인증 객체에 사용자에게 입력받은 정보를 넣어 인증 객체(Authentication
)를 생성한 후 AuthenticationManager
에게 인증을 요청합니다. AuthenticationManager
는 AuthenticationProvider
에게 실질적으로 인증 로직을 수행하도록 합니다. AuthenticationProvider
는 UserDetailsService
를 통해서 해당 정보의 유효를 검사한 후에 인증된 정보일 경우 SecurityContextHolder
의 SecurityContext
안에 인증된 정보를 가진 Authentication
객체를 저장합니다.SessionManagementFilter
의 내용 또한 수행하는데, ConcurrentSession
을 확인해서 세션 최대 허용 개수를 초과하는 경우 두 전략 중 하나를 선택합니다SessionAuthenticationException
session.expireNoe
SessionFixation
에서 세션 고정 보호를 위해서 새롭게 인증된 사용자에게 새로운 쿠키를 발급합니다. 마지막으로 Register SessionInfo
해당 사용자의 정보가 세션에 등록됩니다.SecurityContextPersistenceFilter
의 HttpSessionSecurityContextRepository
가 최종 인증된 사용자의 정보를 가지고 있는 SecurityContext
정보를 Session
에 저장하고, 해당 SecurityContext
는 삭제합니다DelegatingFilterProxy
가 사용자의 인증 요청을 받고, FilterChainProxy
에 위임합니다FilterChainProxy
는 관련 filter
들의 목록을 가지고 있는 상태이므로, 위임을 받으면 해당 필터들을 순서대로 호출합니다.SecurityContextPersistenceFilter
는 HttpSessionSecurityContextRepository
가 로직을 수행하도록 하고, HttpSessionSecurityContextRepository
는 loadContext
를 실행합니다. 이전 인증으로 Session
에 저장된 SecurityContext
가 존재하므로, context
를 새로 생성하지 않고, 다음 필터로 넘어갑니다.LogoutFilter
와 인증 필터는 지나갑니다(인가 처리이므로)ConcurrentSessionFilter
또한 동시적인 세션을 처리하기 위한 필터이고, 현재는 동시적인 세션 검증을 통과한 인증된 사용자이므로 지나갑니다.RememberMeAuthenticationFilter
는 현재 사용자의 Session
이 만료되거나 유실되어서, SecurityContext
안의 Authentication
객체의 값이 null
인 상태로, header
에 remember-me
라는 값을 가지고 있을 때 작동하는 필터이므로 지나갑니다AnonymousAuthenticationFilter
는 현재 사용자가 인증을 시도하지 않고, 어떠한 권한도 없는 상태에서 바로 자원을 요청하는 경우에 동작하는 필터이므로 지나갑니다SessionManagementFilter
는 현재 요청의 Session
에 SecurityContext
가 없거나 null일 경우 동작하는 필터이므로 마찬가지로 지나갑니다ExceptionTranslationFilter
는 인증, 인가의 예외 처리를 하는 필터이므로, doFilter
메소드를 try-catch문으로 감싼 후 다음 필터로 이동합니다.FilterSecurityInterceptor
는 현재 요청에서 두 가지를 확인합니다.AuthenticationException
을 발생시킵니다.(예외는 이전 ExceptionTranslationFilter
에서 처리합니다)AccessDecisionManager
가 AccessDecisionVoter
의 승인, 거부 결과가 거부라면 AccessDeniedException
을 발생시킵니다.