Spring Security ‐ 인증 아키텍처 이해 - woojin-playground/Backend-PlayGround GitHub Wiki
인증 아키텍처
인증 - Authentication
Authentication은 특정 자원에 접근하려는 사람의 신원을 확인하는 방법을 의미한다.- 사용자 인증의 일반적인 방법은 사용자 이름과 비밀번호를 입력하게 하는 것으로서 인증이 수행되면 신원을 알고 권한 부여를 할 수 있다.
Authentication은 사용자 인증 정보를 저장하는 토큰 개념 객체로 활용되며 인증 이후SecurityContext에 저장되어 전역적으로 참조가 가능하다.
getAuthorities(): 인증 주체에게 부여된 권한getCredentials(): 인증 주체가 올바른 것을 증명하는 자격 증명으로서 대개 비밀번호를 의미getDetails(): 인증 요청에 대한 추가적인 세부 사항을 저장
getPrincipal(): 인증 주체를 의미하며 인증 요청의 경우 사용자 이름을, 인증 후에는 UserDetails 타입의 객체가 된다.isAuthenticated(): 인증 상태 반환setAuthenticated(boolean): 인증 상태를 설정
- 공식문서의 내용을 미루어 보았을 때, ROLE Based Authority에서는 접두사
ROLE_가 기본적으로 붙게 된다.
인증 컨텍스트 - SecurityContextHolder
-
SecurityContext
Authentication저장 - 현재 인증된 사용자의Authentication객체를 저장한다.- ThreadLocal 저장소 사용 -
SecurityContextHolder를 통해 접근되며 ThreadLocal 저장소를 사용해 각 쓰레드가 자신만의 보안 컨텍스트를 유지할 수 있다. - 애플리케이션 전반에 걸친 접근성 - 애플리케이션 어느 곳에서나 접근 가능하며, 현재 사용자 인증 상태나 권한 상태를 확인하는 데 사용된다.
-
SecurityContextHolder
SecurityContext저장 - 현재 인증된 사용자의Authentication객체를 담고 있는SecurityContext객체를 저장한다.- 전략 패턴 사용 - 다양한 저장 전략을 지원하기 위해
SecurityContextHolderStrategy인터페이스를 사용한다. - 기본 전략 -
MODE_THREADLOCAL - 전략 모드 직접 지정도 가능 -
SecurityContextHolder.setStrategyName(String)
-
SecurityContextHolder 저장 모드
MODE_THREADLOCAL: 기본 모드로, 각 쓰레드가 독립적인 보안 컨텍스트를 가진다. 대부분의 서버 환경에 적합하다.MODE_INHERITABLETHREADLOCAL: 부모 쓰레드로부터 자식 쓰레드로 보안 컨텍스트가 상속되며, 작업을 쓰레드 간 분산 실행하는 경우 유용할 수 있다.MODE_GLOBAL: 전역적으로 단일 보안 컨텍스트를 사용하며 서버 환경에서는 부적합하며 간단한 애플리케이션에서나 적합하다.
SecurityContext를 참조 ->SecurityContextHolder.getContextHolderStrategy().getContext()SecurityContext를 제거 ->SecurityContextHolder.getContextHolderStrategy().clearContext()
❗쓰레드마다 할당되는 전용 저장소에
SecurityContextHolder를 저장하기 때문에 동시성 문제가 없다. ❗쓰레드 풀에서 운용되는 쓰레드일 경우 새로운 요청이더라도 기존의 쓰레드 풀에서 재사용될 수 있기 때문에 클라이언트로 응답 직전에SecurityContext를 항상 삭제해주고 있다.
인증 관리자 - AuthenticationManager
-
AuthenticationManager
- 인증 필터로부터
Authentication객체를 전달받아 인증을 시도해 인증에 성공할 경우 사용자 정보, 권한 등을 포함한 완전히 채워진Authentication객체를 반환한다. AuthenticationManager는 여러AuthenticationProvider들을 관리하며AuthenticationProvider목록을 순차적으로 순회하며 인증 요청을 한다.AuthenticationProvider목록 중에서 인증 처리 요건에 맞는 적절한AuthenticationProvider를 찾아 인증 처리를 위임한다.AuthenticationManagerBuilder에 의해 객체가 생성되며 주로 사용하는 구현체는ProviderManager가 있다.
- 인증 필터로부터
-
AuthenticationManagerBuilder
AuthenticationManager객체를 생성하며,UserDetailsService및AuthenticationProvider를 추가할 수 있다.HttpSecurity.getSharedObject(AuthenticationManagerBuilder.class)를 통해 객체를 참조할 수 있다.
- AuthenticationManager가 여러 AuthenticationProvider 목록들을 가지고 적합한 곳에 요청 처리를 위임한다.
- 만약 요청 처리를 위임할 수 있는 provider가 없다면 ProviderNotFoundException 예외가 발생하면서 인증에 실패한다.
❗
HttpSecurity를 사용할 경우build()는 최초 한 번만 호출되어야 하고 그 이후에는getObject()로 참조해야 한다. 그 이유는build()가 호출되면 내부적으로 여러 Security Filter들이 중복 등록되거나 설정이 개발자가 의도한대로 동작하지 않을 수 있기 때문이다.
인증 제공자 - AuthenticationProvider
- 사용자 자격증명을 확인하고 인증 과정을 관리하는 클래스로서 사용자가 시스템에 액세스하기 위해 제공한 정보가 유효한가를 판단한다.
- 다양한 유형의 인증 메커니즘을 지원할 수 있는데 예를 들어 표준 사용자 이름과 비밀번호를 기반으로 하는 인증, 토큰 기반 인증, 지문 인식 등을 처리할 수 있다.
- 성공적인 인증 후에는
Authentication객체를 반환하며 이 객체는 사용자 신원 정보와 인증된 자격증명을 포함한다. - 인증 과정 중에 문제가 발생하면
AuthenticationException예외를 발생시켜 문제를 알리는 역할을 수행한다.
사용자 상세 - UserDetails
- 사용자 기본 정보를 저장하는 인터페이스로서
Spring Security에서 사용하는 사용자 타입이다. - 저장된 사용자 정보는 추후에 인증 절차에서 사용되기 위해
Authentication객체에 포함되며 구현체로서User클래스가 제공된다.
isCredentialsNonExpired(): 사용자 비밀번호가 유효 기간이 지났는지를 확인하며 유효 기간이 지난 비밀번호는 인증할 수 없다.isAccountNonExpired(): 사용자 계정이 유효 기간이 지났는지를 나타내며 기간이 만료되었다면 인증할 수 없다.getUsername(): 사용자 인증에 사용된 사용자 이름을 반환하며 null을 반환할 수 없다.isAccountNonLocked(): 사용자가 잠겨있는지 아닌지를 나타내며 잠긴 사용자는 인증이 불가능하다.getPassword(): 사용자 인증에 사용된 비밀번호를 반환한다.isEnabled(): 사용자가 활성화되었는지를 나타내며 비활성화된 사용자는 인증할 수 없다.
사용자 상세 서비스 - UserDetailsService
UserDetailsService의 주요 기능은 사용자와 관련된 상세 데이터를 로드하는 것이며, 사용자의 신원, 권한, 자격 증명 등과 같은 정보를 포함할 수 있다.- 이 인터페이스를 사용하는 클래스는 주로
AuthenticationProvider이며, 사용자가 시스템에 존재하는지 여부와 사용자 데이터를 검색하고 인증 과정을 수행한다.