스프링 시큐리티(Spring Security)는 웹 애플리케이션의 보안을 책임지는 강력한 프레임워크로, 다양한 보안 필터를 통해 인증(Authentication)과 인가(Authorization)을 처리합니다. 아래에서는 스프링 시큐리티의 기본적인 인증 흐름과 필터들, 그리고 내부에서 인증 정보를 저장하는 과정을 디테일하게 설명하겠습니다.
AuthorizationFilter (인가 필터): URL에 대한 접근 제한을 관리하는 필터입니다. 이 필터는 사용자가 요청한 URL에 대해 해당 사용자가 적절한 권한을 가지고 있는지 확인합니다. 사용자가 권한이 없으면, 접근을 거부합니다.
DefaultLoginPageGeneratingFilter: 디폴트 로그인 페이지를 생성하는 필터입니다. 만약 애플리케이션에서 사용자 정의 로그인 페이지를 제공하지 않는다면, 스프링 시큐리티는 이 필터를 통해 기본 로그인 페이지를 자동으로 생성합니다.
UsernamePasswordAuthenticationFilter: 사용자가 로그인 폼을 통해 제출한 사용자명과 비밀번호를 처리하는 필터입니다. 이 필터는 사용자 자격 증명(Username and Password)을 받아 UsernamePasswordAuthenticationToken을 생성합니다.
UsernamePasswordAuthenticationToken: 사용자가 로그인 시 입력한 사용자명과 비밀번호를 포함하는 토큰입니다. 이 토큰은 Authentication 인터페이스를 구현하며, 스프링 시큐리티 내부에서 인증의 기본 단위로 사용됩니다.
AbstractAuthenticationToken: UsernamePasswordAuthenticationToken의 상위 클래스입니다. 이 클래스는 인증 관련 정보를 캡슐화하고 있으며, 인증이 완료되면 사용자의 권한 정보가 포함됩니다.
Authentication: 스프링 시큐리티에서 인증을 나타내는 인터페이스입니다. 인증이 완료된 후, 이 인터페이스를 구현한 객체가 SecurityContext에 저장됩니다.
ProviderManager: 여러 AuthenticationProvider를 관리하며, 인증 요청이 들어오면 이들 중 하나에 인증을 위임합니다. ProviderManager는 for문을 사용하여 각각의 AuthenticationProvider에 인증을 시도하며, 인증이 성공하면 Authentication 객체를 반환합니다.
4. UserDetailsService와 PasswordEncoder
스프링 시큐리티에서 사용자 정보를 관리하고 비밀번호를 처리하는 핵심 컴포넌트는 UserDetailsService와 PasswordEncoder입니다.
InMemoryUserDetailsManager: 메모리 내에서 사용자 정보를 관리하는 구현체로, 테스트나 작은 규모의 애플리케이션에 적합합니다. UserDetailsService를 구현하며, 사용자 이름으로 사용자를 로드하고, 해당 사용자의 세부 정보를 반환합니다.
PasswordEncoder: 비밀번호를 암호화하고 비교하는 데 사용되는 인터페이스입니다. NoOpPasswordEncoder는 비밀번호를 평문으로 저장하는 간단한 구현체입니다. 실제 운영 환경에서는 BCryptPasswordEncoder와 같은 강력한 인코더를 사용하는 것이 일반적입니다.
DaoAuthenticationProvider: AuthenticationProvider의 한 구현체로, UserDetailsService를 사용하여 사용자 정보를 로드하고, PasswordEncoder를 통해 비밀번호를 검증합니다. 인증이 성공하면 Authentication 객체를 반환합니다.
사용자 요청: 사용자가 보호된 리소스에 접근하려고 시도합니다.
필터 체인: AuthorizationFilter와 DefaultLoginPageGeneratingFilter가 요청을 가로채고, 인증 여부를 확인합니다.
자격 증명 추출: UsernamePasswordAuthenticationFilter가 로그인 폼에서 사용자명과 비밀번호를 추출하여 UsernamePasswordAuthenticationToken을 생성합니다.
AuthenticationManager: ProviderManager가 인증 요청을 받아, DaoAuthenticationProvider를 통해 실제 인증을 시도합니다.
UserDetailsService와 PasswordEncoder: DaoAuthenticationProvider가 InMemoryUserDetailsManager를 통해 사용자 정보를 로드하고, PasswordEncoder를 통해 비밀번호를 검증합니다.
인증 성공: 인증이 성공하면 Authentication 객체가 생성되어 SecurityContext에 저장됩니다.
응답: 인증이 완료되면, 요청된 리소스에 접근할 수 있도록 응답이 반환됩니다.
이 흐름을 다이어그램으로 나타내면 다음과 같습니다:
Request →
Filters (AuthorizationFilter, DefaultLoginPageGeneratingFilter) →
Token (UsernamePasswordAuthenticationToken) →
Authentication Manager (ProviderManager) →
Provider (DaoAuthenticationProvider) →
User Details Service (InMemoryUserDetailsManager) →
Validation (PasswordEncoder) →
Authentication Result →
Response
스프링 시큐리티 구성 방법 (Lambda DSL)
Spring Security 6.1 이후, Spring Boot 3.1.0에서는 보안 구성을 더 간결하게 하기 위해 람다 DSL(Lambda DSL) 스타일을 권장합니다. 이 스타일은 기존 방식에 비해 코드 가독성을 높이고, 불필요한 중복을 줄이는 장점이 있습니다.
예제: 람다 DSL을 사용한 보안 설정
기존 방식:
java
코드 복사
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
람다 DSL 적용:
java
코드 복사
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
formLogin {
loginPage = "/login"
permitAll()
}
}
람다 DSL을 적용하면, 코드는 더 읽기 쉽게 정돈되며, .and()와 같은 연결 메소드를 생략할 수 있습니다. 이로 인해 보안 설정을 더 간결하게 작성할 수 있습니다.
이 설명을 통해 스프링 시큐리티의 필터 체인, 인증 흐름, 주요 컴포넌트 및 새로운 람다 DSL 스타일에 대한 이해를 높일 수 있습니다. 스프링 시큐리티를 사용하여 웹 애플리케이션의 보안을 효과적으로 관리하고 설정할 수 있을 것입니다.