Spring Security란?
- 스프링 시큐리티(Spring Security)는 스프링 기반 어플리케이션의 보안(인증과 권한, 인가)을 담당하는 스프링 하위 프레임워크
- 보안과 관련해서 체계적으로 많은 옵션들을 제공해주기 때문에 개발자의 입장에서는 하나하나 보안 관련 로직을 작성하지 않아도 된다는 장점이 있다.
Spring Security 용어
- 인증(Authentication) : 해당 사용자가 본인이 맞는지를 확인하는 절차
- 인가(Authorization) : 인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차
- 접근 주체(Principal) : 보호받는 Resource에 접근하는 대상
- 비밀번호(Credential) : Resource에 접근하는 대상의 비밀번호
- 권한 : 인증된 주체가 어플리케이션의 동작을 수행할 수 있도록 허락되어 있는 지를 결정
- 인증 과정을 통해 주체가 증명된 이후 권한을 부여할 수 있다.
- 권한 부여에도 두 가지 영역이 존재하는데 웹 요청 권한과 메서드 호출 및 도메인 인스턴스에 대한 접근 권한 부여
인증(Authentication) -> 인증 성공 후 -> 인가(Authorization)
- Spring Security는 기본적으로 인증 절차를 거친 후에 인가 절차를 진행하게 되며, 인가 과정에서 해당 리소스에 대한 접근 권한이 있는지를 확인하게 된다.
- 이러한 인증과 인가를 위해 Principal을 아이디로, Credential을 비밀번호로 사용하는 인증 방식을 사용한다.
- 기본적으로 인증 정보는 인메모리 세션 저장소인 SecurityContextHolder에 세션 - 쿠키 방식으로 저장
Spring Security 동작 과정
HTTP 요청 수신
- 클라이언트(브라우저)로 부터 요청(Request)이 오면 인증 및 권한 부여 목적으로 일련의 필터를 거친다.
- 일반적으로 ID, PASSWORD 기반의 인증이라고 할 경우 가장 먼저 Application Filters라는 필터 뭉치에 도달한다.
- 다음으로 username, password를 사용하는 form 기반 인증을 처리하는 필터인 UsernamePasswordAuthenticationFilter에 도착하게 된다.
- id, password가 아닌 OAuth2.0 인증이나 JWT를 이용한 인증을 하려고 할 때는 해당 필터가 아닌 다른 필터를 거치게 된다.(ex:OAuth2ClientAuthenticationProcessingFilter)
- UsernamePasswordAuthenticationFilter에 요청이 도착하면 해당 클래스의 attempAtuhentication(request, response) 메서드가 동작.
- 이 메서드는 request로부터 username, password를 가지고 와서 사용자 자격 증명을 기반으로 한 UsernamePasswordAuthenticationToken(Authentication)을 생성한다.
- UsernamePasswordAuthenticationToken(Authentication)을 가지고 AuthenticationManager(실질적으로 구현체인 ProviderManager)에게 인증을 진행하도록 위임.
- UsernamePasswordAuthenticationToken은 Authentication 인터페이스의 구현체이다. 객체 간의 구조를 뜯어보면 UsernamePasswordAuthenticationToken이 AbstractAuthenticationToken을 extends, AbstractAuthenticationToken은 Authentication을 implements하는 구조로 설계되어 있음
- 모든 접근 주체는 Authentication을 생성. 이것은 최종적으로 SecurityContext에 보관되고 사용
AuthencationManager(Interface)
- Authentication 객체를 받아 인증하고, 인증되었다면 인증된 Authentication 객체를 돌려주는 authenticate()메서드를 구현하도록 하는 인터페이스
- 이 메서드를 통해 인증이 되면 isAuthenticated(boolean) 값을 true로 바꿔준다.
ProviderManager(Class)
- AuthenticationManager의 구현체로 스프링에서 인증을 담당하는 클래스
- 스프링 시큐리티가 생성, 등록하고 관리하는 스프링 빈이기 때문에 직접 구현할 필요는 없음.
- ProviderManager 클래스는 인증을 담당하고 있지만 실제로 인증 과정을 진행하는게 아니라 멤버 변수로 가지고 있는 AuthenticationProvider에게 인증을 위임하고 그 중에서 인증 처리가 가능한 AuthenticationProvider 객체가 인증 과정을 거쳐서 인증에 성공하면 요청에 대해 ProviderManager가 인증이 되었다고 알려주는 방식
- 인증이 되었다고 알려주는 건 AuthenticationManager 인터페이스의 authenticate() 메서드의 리턴 값인 Authentication 객체 안에 인증 값을 넣어주는 것으로 처리
AuthenticationProvider (Interface)
-
AuthenticationManager와 같은 authenticate() 메서드를 통해 인증 과정이 진행
-
supports(Class<?>) 메서드는 앞에서 필터를 통해 보내준 Authentication 객체를 이 AuthenticationProvider가 인증 처리가 가능한 클래스인지를 확인하는 메서드
-
AuthenticationProvider가 인터페이스이기 때문에 해당 인터페이스를 구현하는 클래스를 만들어 인증처리를 하는 로직을 구현하면 된다.(UserDetailService, UserDetail 등등)
-
이렇게 구현된 인증 절차를 통해 인증이 완료되면 Authentication을 SecurityContextHolder 객체 안의 SecurityContext에 저장
SecurityContextHolder
- SecurityContext 객체를 저장하고 감싸고 있는 wrapper 클래스로 SecurityCotextHolder는 보안 주체의 세부 정보를 포함하여 응용 프로그램의 현재 보안 콘텍스트에 대한 세부 정보가 저장되어 있음
SecurityContext
- Authentication을 보관하는 역할을 하며, SecurityContext를 통해 Authentication 객체를 꺼내올 수 있다.
- ThreadLocal에 저장되어 아무 곳에서나 참조가 가능하도록 설계
DelegatingFilterProxy
- Spring security를 설정하게 된다면 DispatcherServlet에 도달하기 전에 서블릿 Filter 구현체에게 걸린다.
- FilterChain들을 Servelt Container 기반의 필터 위에서 동작하기 위해 중간 연결을 위한 DelegatingFilterProxy를 사용
- DelegatingFilterProxy는 IOC 컨테이너에서 관리하는 빈이 아닌 표준 서블릿 필터를 구현하고 있으며 내부적으로 요청을 위임할 (FilterChainProxy)을 가지고 있다.
- DelegatingFilterProxy은 Servlet Container 기반의 필터 위에서 동작하기 위해서 중간 역할만 하고 FilterChainProxy에게 요청 처리를 위임
참고자료
https://wildeveloperetrain.tistory.com/50
https://k3068.tistory.com/88