Spring Security를 사용하기 전에는 관리자 기능을 구현하기 위해 사용자의 로그인 정보를 이용해서 구차하게 코딩을 했을 텐데 Spring Security 프레임워크를 사용한다면 관련 처리가 설정을 통해 가능하다.
컨테이너에 무관하게 적용
Spring Security는 WAR나 EAR로도 배포될 수 있으며 JAR 형태의 stand alone으로도 실행이 가능하다.
Spring Security는 Filter를 이용하여 동작한다.
관점의 분리
Spring Security는 보안적인 이슈와 비지니스 로직을 잘 분리시켜 놓았다. 복잡한 트랜젝션 처리를 위해 단순히 @Transactional 만 선언하면 끝났던 것을 생각해보자. @Secured만 사용하면 쉽게 중요한 리소스들을 보호할 수 있다.
유연성
Spring Security는 basic, form, cookie 등 다양한 인증 방법을 사용할 수 있다.
사용자 정보를 저장할 때도 properties, file, RDBMS, LDAP 등을 다양하게 이용할 수 있다.
핵심 컨셉
기본 용어
Security를 이해하기 위해 필수적인 용어들에 대해서 알아보자.
Principal: 인증된 사용자 또는 이를 식별하는 정보
사용자, 디바이스, 시스템 등 행위를 하는 주체를 의미한다.
사용자 정보를 담고 있는 객체로 사용자의 역할이나 권한 정보를 포함할 수 있다.
Secured Resource: 보안이 적용되는 리소스로 URL, API 엔드포인트, 파일 등
일반적으로 메서드를 통해서 보호된다.
Authentication: 인증
Principal이 믿을 수 있는지 파악하는 것으로 일반적으로 id/password를 체크하는 것을 의미한다.
Authorization: 권한
인증이 완료된 Principal이 어떤 행위를 할 권한이 있는지 확인한다. 예를 들어 공동현관문을 통과했다(인증확보)고 해서 모든 집에 들어갈 수 있는 것은 아니다.
ADMIN, MEMBER, GUEST등 role에 기반해서 접근 가능한 자원
이들을 엮어보면Security란 Authentication이 완료된 Principal이 Secured Item을 사용하기 적절한 Authorization을 가지고 있는지 확인하는 것이다.
Spring Security의 구조
Spring Security는 크게 다음의 3가지 요소가 연동된다.
Authentication Manager: 인증 처리 인터페이스
기본 구현체는 ProviderManager이며 어떤 식으로 인증할 것인지에 따라 하위 클래스를 작성한다.
인증 성공 시 인증 정보를 담고 있는 Authentication 객체를 리턴해준다.
인증 실패 시는 AuthenticationException을 발생시킨다.
SecurityContext
현재 사용자에 대한 Authentication 정보가 저장되는데 여기에는 Principal과 Authorities가 포함된다.
Authentication Manager: 권한 처리 인터페이스
인증된 사용자가 Secured Item에 접근할 수 있는 권한이 있는지 확인한다.
Spring Security Framework 동작 흐름
다음은 Servlet 애플리케이션에서 동작하는 Spring Security Framework의 동작 흐름이다.
먼저 사용자를 인증하는 절차이다.
사용자의 request는 DelegatingFilterProxy를 거쳐 Spring의 Security Filter Chain으로 전달되고 일련의 filter들이 인증과 인가에 대한 전처리를 수행한다.
이중 XXXAuthenticationFilte는 AuthenticationManager를 이용해 사용자 인증을 처리한다. AuthenticationManager는 인증 방식에 따라 다양한 ProviderManager 구현체를 가진다.
AuthenticationManager는 인증 방법에 따라 다양한 AuthenticationProvider를 갖는데 이들에게 로그인 요청 정보를 전달한다.
AuthenticationProvider 중 DaoAuthenticationProvider는사용자 정보를 관리하는 UserDetailsService에 사용자의 로그인 정보를 전달하여 인증을 요청한다.
인증에 성공하면UserDetailsService는 UserDetails 객체를 Authentication에 담아Security Context에 저장한다. 만약 인증에 실패한 경우 AuthenticationException이 발생하고 일반적으로 다시 인증을 요청하는 형태로 흘러간다.Security Context는 SecurityContextHolder를 통해 접근할 수 있으며 session 영역에 사용자의 정보를 저장한다.
다음으로 인증이 된 상태에서 인가 절차에 대해서 살펴보자.
AuthorizationFilter는 세션에 저장된 SecurityContext에서 인증 정보를 획득한다.
인증 정보는AuthorizationManager에게 넘겨져서 로그인한 사용자의 권한을 확인한다. 만약 사용자가 권한이 있다면 다음 흐름으로 진행하고 그렇지 않은 경우 AccessDeniedException이 발생한다.
이 과정에서실제 인증 즉 id/password 비교를 담당하는 녀석은UserDetailsService이다.
UserDetailsService는 loadUserByUsername() 메서드를 통해 DB 또는 메모리에 있는실제 저장된 데이터를 기반으로 UserDetails를 반환하는데 이 값과 화면에서 사용자가 입력한 값을 비교하여 인증 여부를 결정한다.