스프링 시큐리티 (Spring Security)는 스프링 기반의 어플리케이션의 보안(인증과 권한, 인가)을 담당하는 스프링 하위 프레임워크. 보안과 관련해 체계적으로 많은 옵션들을 제공해주기 때문에 개발자의 입장에서는 하나하나 보안 관련 로직을 작성하지 않아도 된다는 장점이 있음.
1-1. Spring Security 관련 용어
인증(Authentication) : 해당 사용자가 본인인지 확인하는 절차
인가(Authorization) : 인증된 사용자가 요청한 자원에 접근 가능한지 결정하는 절차
접근 주체(Principal) : 보호받는 Resource에 접근하는 대상
비밀번호(Credential) : Resource에 접근하는 대상의 비밀번호
권한 : 인증된 주체가 어플리케이션의 동작을 수행할 수 있도록 허락되어 있는지 결정
인증 과정을 통해 주체가 증명된 이후 권한을 부여할 수 있다.
권한 부여에 두 가지 영역이 존재하는데 웹 요청 권한과 메서드 호출 및 도메인 인스턴스에 대한 접근 권한 부여가 있다.
기본적으로 인증(Authentication) → 인증성공 → 인가(Authorization)
Spring Security는 기본적으로 인증 절차를 거친 후에 인가 절차를 진행하게 되며, 인가 과정에서 해당 리소스에 대한 접근 권한이 있는지를 확인하게 됩니다. 이러한 인증과 인가를 위해 Principal을 아이디로, Credential을 비밀번호로 사용하는 인증 방식을 사용합니다.
기본적으로 인증 정보는 최종적으로 인메모리 세션 저장소인 SecurityContextHolder에 세션 - 쿠키 방식으로저장
2. Spring Security 동작원리
2-1 . Spring Security 동작원리 상세 (1~4)
동작원리의 1-4까지 흐름
1. 요청 (Request) 사용자가 로그인 정보를 요청한다. 예시) 로그인페이지에서 아이디와 비밀번호를 입력한다. 그럼 아이디와 비밀번호가 서버로 전송하게 된다.
AuthenticationFilter 는 사용자의 세션 ID(JSESSIONID)가 Security Context에 있는지 확인한다. (여기서 Security Context란 아래의 모든 로직을 통과한 인증된 사용자의 정보(인증 개체)를 저장하는 공간이다.) Security Context에 세션 ID가 없다면 아래 로직을 수행한다.
2. 토큰 생성
사용자가 보낸 아이디와 비밀번호를AuthenticationFilter가 받아서 UsernamePasswordAuthenticationToken 토큰(인증용 객체)을 생성한다.
AuthenticationFilter가 받고 아이디와 비번을 UsernamePasswordAuthenticationToken 토큰(인증용 객체)에 전달
3. 생성된UsernamePasswordAuthenticationToken는 AuthenticationManager의 인증 메서드를 호출하는 데 사용된다. 여기서 AuthenticationManager는 단순한 인터페이스이며 실제 구현은 ProviderManager이다.
ProviderManager 에는 사용자 요청을 인증에 필요한 AuthenticationProvdier 목록이 있다.
ProviderManager는 제공된 각 AuthenticationProvdier를 살펴보고 전달된 인증 개체(UPAT)를 기반으로 사용자 인증을 시도한다.
4. 토큰을 처리할 수 있는 AuthenticationProvider 선택 AuthenticationManager는 List 형태로 AuthenticationProvider들을 가지고 있는데, 실제로 인증을 할 AuthenticationProvider에게 인증용 객체를 다시 위임한다. 예시) AuthenticationManager는 인증을 담당하는 클래스이지만, 진짜는 AuthenticationManager가 가지고 있는 AuthenticationProvider 인터페이스들한테 "이거(인증용 객체) 줄테니 한번 인증처리가 가능한 AuthenticationProvider는 인증처리 해줘" 라고 또 떠넘기는 방식이다.
AuthenticationProvider의 리스트 중 일부
CasAuthenticationProvider
JaasAuthenticationProvider
DaoAuthenticationProvider
OpenIDAuthenticationProvider
RememberMeAuthenticationProvider
LdapAuthenticationProvider
2-2 . Spring Security 동작원리 상세 (5~7)
동작원리의 5~7까지 흐름
5. AuthenticationProvider는 사용자의 이름(username)을 기반으로 사용자의 세부정보를 검색하기위해 UserDetailsService를 사용할 수 있다.
5-1. AuthenticationProvider 인터페이스에서는 authticate() 메서드를 오버라이딩해 인증용객체를 파라미터로 받아 로그인페이지에서 입력한 사용자의 정보를 들고 올 수 있다.
UserDetailsService는 Spring Security의 인터페이스 이며 이를 구현한 서비스는 직접개발 해야한다.
즉, 아래 코드의 loadUserByUsername메소드를 오버라이딩해DB와 비교하는 로직을 직접개발 해야한다.
public interface UserDetailService {
UserDetail loadUserByUsername(String username) throws UsernameNotFoundException;
}
8. AuthenticationProvider인터페이스에 의해 사용자가 성공적으로 인증되면, 완전히 채워진 인증개체가 반환된다. 인증에 실패하면 AuthenticaionException이 발생한다. AuthenticaionException이 발생하면 인증 메커니즘을 지원하는 AuthenticationEntryPoint에 의해 처리된다.
사용자의 정보가 존재하지 않는경우 예외를 던진다고한다. (아래 코드 참고)
9. 인증완료
AuthenticationManager는 획득한 완전히 채워진 인증개체를 관련 인증 필터(AuthenticationFilter)로 다시 반환한다
10. SecurityContext에서 인증 개체 설정
AuthenticationFilter는 향후 필터 사용을 위해 획득한 인증 개체를 SecurityContext에 저장한다.
SecurityContext는 1번 로직에서 설명 했었다. 이 SecurityContext에 인증개체가 있는지를 확인하고,
인증되지 않은 사용자와 인증된 사용자의 요청을 감시하고, AuthenticationManager에게 인증처리를 맡김 인증이 성공한다면 인증용 객체를 AuthenticationContext 객체에 저장하고 AuthenticationSuccessHandler 실행 실패 시 AuthenticationFailureHandler 실행
AuthenticationProvider
로그인페이지에서 입력한 정보와 DB에 저장된 정보를 비교 일치하다면 Authentication객체로 담아 AuthenticationManager에게 전달 만약 DB에 저장된 정보들 중 로그인페이지에서 입력한 정보와 일치하는 정보가 없다면 UsernameNotFoundException예외 처리
UserDetailsService
DB에서 유저정보를 가져오는 역할담당
UserDetails
사용자의 정보를 담은 인터페이스이며 직접 상속받아 사용한다. 유저객체에서 UserDetails를 상속받게 되면 구현해야할 메서드들이 있으며, 각 메서드들의 정의는 다음과 같다.