스프링 시큐리티는 스프링 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크입니다. 즉 인증(Authenticate, 누구인지?) 과 인가(Authorize, 어떤것을 할 수 있는지?)를 담당하는 프레임워크를 말합니다.
스프링 시큐리티에서는 주로 서블릿 필터(filter)와 이들로 구성된 필터체인으로의 구성된 위임모델을 사용합니다. 그리고 보안과 관련해서 체계적으로 많은 옵션을 제공해주기 때문에 개발자 입장에서는 일일이 보안관련 로직을 작성하지 않아도 된다는 장점이 있습니다. 예) CORS 등
스프링 시큐리티를 알아보기 전에 먼저 간단히 스프링의 동작과정 부터 간단히 살펴보고 가겠습니다.
스프링 시큐리티는 인증 관리자(Authentication Manager)와 접근 결정 관리자(Access Decision Manager)를 통해 사용자의 리소스 접근을 관리합니다.
인증 관리자는 UsenamePasswordAuthenticationFilter
, 접근 결정 관리자는 FilterSecurityInterceptor
가 수행 합니다.
SecurityContextPersistenceFilter
: SecurityContextRepository에서 SecurityContext를 로드하고 저장하는 일을 담당함 LogoutFilter
: 로그아웃 URL로 지정된 가상URL에 대한 요청을 감시하고 매칭되는 요청이 있으면 사용자를 로그아웃시킴 UsernamePasswordAuthenticationFilter
: 사용자명과 비밀번호로 이뤄진 폼기반 인증에 사용하는 가상 URL요청을 감시하고 요청이 있으면 사용자의 인증을 진행함 DefaultLoginPageGeneratingFilter
: 폼기반 또는 OpenID 기반 인증에 사용하는 가상URL에 대한 요청을 감시하고 로그인 폼 기능을 수행하는데 필요한 HTML을 생성함 BasicAuthenticationFilter
: HTTP 기본 인증 헤더를 감시하고 이를 처리함 RequestCacheAwareFilter
: 로그인 성공 이후 인증 요청에 의해 가로채어진 사용자의 원래 요청을 재구성하는데 사용됨 SecurityContextHolderAwareRequestFilter
: HttpServletRequest를 HttpServletRequestWrapper를 상속하는 하위 클래스(SecurityContextHolderAwareRequestWrapper)로 감싸서 필터 체인상 하단에 위치한 요청 프로세서에 추가 컨텍스트를 제공함 AnonymousAuthenticationFilter
: 이 필터가 호출되는 시점까지 사용자가 아직 인증을 받지 못했다면 요청 관련 인증 토큰에서 사용자가 익명 사용자로 나타나게 됨 SessionManagementFilter
: 인증된 주체를 바탕으로 세션 트래킹을 처리해 단일 주체와 관련한 모든 세션들이 트래킹되도록 도움 ExceptionTranslationFilter
: 이 필터는 보호된 요청을 처리하는 동안 발생할 수 있는 기대한 예외의 기본 라우팅과 위임을 처리함 FilterSecurityInterceptor
: 이 필터는 권한부여와 관련한 결정을 AccessDecisionManager에게 위임해 권한부여 결정 및 접근 제어 결정을 쉽게 만들어 줌이떄 RestAPI에서는 로그인폼이라던지 View가 없기 때문에
UsernamePasswordAuthenticationFilter
작동하기 전에 Json으로 인증권한이 없다는 오류를 보내야합니다.
모든 접근 주체(=유저) 는 Authentication 를 생성하는데 이것은 SecurityContext
에 보관되고 사용됩니다.
즉 security의 세션들은 내부 메모리(SecurityContextHolder)에 쌓고 꺼내쓰는 것입니다. 참고로 Authentication 인터페이스는 매우 매우 자주 사용함으로 알아둬야합니다.
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities(); // Authentication 저장소에 의해 인증된 사용자의 권한 목록
Object getCredentials(); // 주로 비밀번호
Object getDetails(); // 사용자 상세정보
Object getPrincipal(); // 주로 ID
boolean isAuthenticated(); //인증 여부
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
이걸 기반으로 사용자 ID 나 상세정보들을 불러올수도 있습니다.
처음에 Request가 들어오면 AuthenticationFilter(UsernamePassAuthenticationFilter
)를 거침 혹은 custom filter를 등록해서 custom filter를 사용할 수도 있음
우리는 요청에 따른 UsernamePasswordAuthenticationToken
을 생성 (Authentication 인터페이스의 구현체입니당)
UsernamePasswordAuthenticationToken
(Token이라고 하겠다.)을 AuthenticationManager에게 이 Token은 옳바른 유저인지 물어봄
AuthenticationManager
는 1개 이상의 Provider를 갖고 있는데, Provider는 Token(Authentication의 구현체) 객체를 적절히 판단하여 인증처리를 함
우리가 직접 구현한 서비스에 해당 유저에게 인증요청을 보냄
인증된 유저라고 판단되면 적절한 User DTO객체를 전달
인증된 결과를 SecurityContextHolder에 저장
이렇게 크나큰 관점에서 보면 이해는 할 것 같습니다.
하지만 막상 코딩하다 보면 머리가 백지가 된다는 점 ㅋㅋㅋㅋ
그리고 기본적으로는 username, password를 통한 인증 방식이긴한데
소셜로그인 같은경우는 password 부분에 다른값을 넣는다던지
아니면 UsernamePasswordAuthenticationToken
을 구현 안하고 다른 방식으로 검증을 하고 인증 진행을 하면 될 것 같습니다.
큰 관점에서는 되게 쉽게 다가오는데
막상 설계를 하려니까 감이 안잡혔습니다.
인증 부분같은 경우들은 Filter, Provider 등을 본인의 입맛대로 Custom 해서 사용할 수 있어서 다양한 인증을 구현할 수 있습니다. 예) Jwt 인증
스프링 시큐리티가 인증 구현하면서 감이 조금밖에 안잡혔는데
공부하고 나서는 어느정도 이해를 해서 구현할때 편한 것 같습니다.
Jwt 인증 구현은 여기 블로그를 참고하면 매우 매우 좋을것 같습니다.
(너무 친절하고 자세히 설명해놓아서 이해하기 너무 편했어요)