1. UserDetailsService
**UserDetailsService**는 Spring Security에서 사용자의 인증을 위한 사용자 정보를 가져오는 데 사용되는 인터페이스입니다. 이 인터페이스는 사용자의 사용자명(username)을 통해 사용자 정보를 조회하고, 그 정보를 바탕으로 인증을 처리합니다.
주요 메서드
• loadUserByUsername(String username): 이 메서드는 사용자명을 입력받아 해당 사용자의 인증 정보를 담고 있는 UserDetails 객체를 반환합니다.
UserDetailsService 구현 예시
보통 데이터베이스와 연결하여 사용자의 인증 정보를 가져오는데, 일반적인 구현 예시를 살펴보겠습니다.
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return new CustomUserDetails(user); // 사용자 정보를 CustomUserDetails로 변환하여 반환
}
}
위 코드에서 CustomUserDetailsService는 사용자명으로 데이터베이스에서 사용자를 조회하고, 이를 UserDetails로 변환하여 Spring Security가 사용할 수 있게 합니다.
UserDetails 인터페이스
• UserDetailsService는 UserDetails 인터페이스와 함께 사용됩니다. UserDetails는 사용자의 아이디, 비밀번호, 권한 등의 정보와 계정 상태(잠김, 만료 여부 등)를 포함하고 있으며, Spring Security에서 사용자의 인증 상태를 확인하는 데 필요한 정보를 담고 있습니다.
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class CustomUserDetails implements UserDetails {
private final User user;
public CustomUserDetails(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities(); // 사용자 권한 목록
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return user.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return user.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return user.isCredentialsNonExpired();
}
@Override
public boolean isEnabled() {
return user.isEnabled();
}
}
2. PasswordEncoder
**PasswordEncoder**는 Spring Security에서 비밀번호를 안전하게 암호화하는 데 사용되는 인터페이스입니다. 비밀번호를 평문으로 저장하지 않고 암호화하여 저장하는 것이 일반적이므로, 비밀번호를 안전하게 처리하는 것이 중요합니다.
Spring Security는 여러 PasswordEncoder 구현체를 제공하며, 대표적인 구현체로는 BCryptPasswordEncoder, Pbkdf2PasswordEncoder, NoOpPasswordEncoder 등이 있습니다.
주요 메서드
• encode(CharSequence rawPassword): 평문 비밀번호를 인코딩하여 암호화된 문자열로 반환합니다.
• matches(CharSequence rawPassword, String encodedPassword): 입력된 평문 비밀번호와 암호화된 비밀번호가 일치하는지 확인합니다.
PasswordEncoder 사용 예시
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class PasswordService {
private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
public String encodePassword(String password) {
return passwordEncoder.encode(password);
}
public boolean matches(String rawPassword, String encodedPassword) {
return passwordEncoder.matches(rawPassword, encodedPassword);
}
}
PasswordEncoder 구현체
1. BCryptPasswordEncoder:
• 가장 많이 사용되는 인코더로, 강력한 암호화 알고리즘을 사용합니다.
• 반복 횟수를 설정해 비밀번호 해싱 속도를 조절할 수 있습니다.
2. Pbkdf2PasswordEncoder:
• PBKDF2 해싱 알고리즘을 사용하며, 성능과 보안이 우수하여 여러 인증 시스템에서 사용됩니다.
3. NoOpPasswordEncoder:
• 암호화 없이 평문으로 비밀번호를 저장합니다. 테스트용으로만 사용해야 합니다.
4. Argon2PasswordEncoder:
• 강력한 해싱 알고리즘인 Argon2를 사용하며, 암호학 대회에서 우수한 성능을 인정받은 인코더입니다.
3. AuthenticationProvider
**AuthenticationProvider**는 Spring Security에서 사용자의 인증을 처리하는 인터페이스입니다. AuthenticationProvider는 주로 UserDetailsService와 PasswordEncoder를 결합하여 사용자의 아이디와 비밀번호를 검증합니다.
주요 메서드
• authenticate(Authentication authentication): 인증 처리 메서드로, 입력된 Authentication 객체를 사용하여 사용자의 자격 증명을 검증합니다.
• supports(Class<?> authentication): 이 AuthenticationProvider가 특정 인증 객체 타입을 지원하는지 여부를 반환합니다.
AuthenticationProvider 구현 예시
아래 예시는 CustomAuthenticationProvider가 UserDetailsService와 PasswordEncoder를 통해 사용자 인증을 처리하는 예제입니다.
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
public CustomAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
this.userDetailsService = userDetailsService;
this.passwordEncoder = passwordEncoder;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
// 사용자 정보 조회
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 비밀번호 검증
if (userDetails != null && passwordEncoder.matches(password, userDetails.getPassword())) {
return new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities());
} else {
throw new AuthenticationException("Authentication failed") {};
}
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
• authenticate 메서드에서 UserDetailsService를 통해 UserDetails를 불러온 후, PasswordEncoder로 입력된 비밀번호와 저장된 비밀번호를 비교하여 검증합니다.
• 검증이 성공하면 UsernamePasswordAuthenticationToken 객체를 반환하여 Spring Security가 인증된 사용자로 인식하게 합니다.
• 실패 시, AuthenticationException을 발생시켜 Spring Security에 인증 실패를 알립니다.
AuthenticationProvider 설정
Spring Security 설정 클래스에서 CustomAuthenticationProvider를 등록해 사용할 수 있습니다.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomAuthenticationProvider customAuthenticationProvider;
public SecurityConfig(CustomAuthenticationProvider customAuthenticationProvider) {
this.customAuthenticationProvider = customAuthenticationProvider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll();
}
}
요약
1. UserDetailsService:
• 사용자 정보를 UserDetails 형식으로 반환해주는 인터페이스로, 사용자의 아이디, 비밀번호, 권한 등 정보를 제공합니다.
• 데이터베이스에서 사용자 정보를 불러오고, 이 정보를 UserDetails 객체로 반환하여 인증에 사용됩니다.
2. PasswordEncoder:
• 비밀번호를 안전하게 암호화하고 검증하는 인터페이스로, 일반적으로 BCryptPasswordEncoder 같은 강력한 암호화 알고리즘을 사용합니다.
• encode 메서드로 암호화하고, matches 메서드로 평문 비밀번호와 암호화된 비밀번호를 비교합니다.
3. AuthenticationProvider:
• UserDetailsService와 PasswordEncoder를 결합하여 사용자의 아이디와 비밀번호를 검증합니다.
• authenticate 메서드를 통해 사용자 인증을 처리하고, 성공 시 UsernamePasswordAuthenticationToken을 반환하여 인증된 상태를 Spring Security에 전달합니다.
이 세 가지는 Spring Security의 핵심 인증 흐름을 구성하는 요소입니다. UserDetailsService가 사용자 정보를 제공하고, PasswordEncoder가 비밀번호를 비교하며, AuthenticationProvider는 이들을 결합하여 사용자의 인증을 담당합니다.
이들의 연관 관계
1. UserDetailsService와 AuthenticationProvider:
• **AuthenticationProvider**는 **UserDetailsService**를 사용하여 사용자 정보를 조회합니다. AuthenticationProvider는 사용자 이름(또는 아이디)을 통해 UserDetailsService의 loadUserByUsername 메서드를 호출하여 UserDetails 객체를 얻습니다.
• 이렇게 얻은 UserDetails 객체에는 사용자 이름, 비밀번호, 권한 등이 포함되어 있으며, 이 정보는 이후 인증 과정에서 사용됩니다.
2. AuthenticationProvider와 PasswordEncoder:
• **AuthenticationProvider**는 UserDetails 객체에서 불러온 암호화된 비밀번호와 사용자가 입력한 평문 비밀번호를 **PasswordEncoder**를 사용해 비교합니다.
• PasswordEncoder의 matches 메서드를 통해 입력된 비밀번호를 암호화하고, 데이터베이스에 저장된 암호화된 비밀번호와 일치하는지 확인하여 인증이 성공적인지 판단합니다.
3. UserDetailsService와 PasswordEncoder의 간접적 연관:
• UserDetailsService는 사용자의 비밀번호를 데이터베이스에서 가져오는 역할만 합니다.
• 직접적으로 PasswordEncoder와 연관이 있는 것은 아니지만, UserDetailsService가 반환하는 UserDetails의 비밀번호를 PasswordEncoder가 검증하는 과정에서 간접적으로 연관됩니다.
연관성 있는 인증 흐름 예시
1. 사용자가 로그인 요청을 하면, AuthenticationProvider는 사용자가 입력한 아이디를 UserDetailsService에 전달하여 사용자 정보를 조회합니다.
2. UserDetailsService는 데이터베이스에서 사용자 정보를 가져와 UserDetails 객체로 반환합니다.
3. AuthenticationProvider는 PasswordEncoder를 사용하여 사용자가 입력한 평문 비밀번호와 UserDetails에 저장된 암호화된 비밀번호를 비교합니다.
4. 비밀번호가 일치하면 AuthenticationProvider는 인증 성공으로 간주하고, 인증된 사용자 정보(권한 등)를 Spring Security의 인증 시스템에 전달합니다.
이처럼 UserDetailsService, PasswordEncoder, AuthenticationProvider는 Spring Security 인증 과정에서 필수적인 역할을 수행하며, 서로 협력하여 안전한 사용자 인증을 제공합니다.
'Spring Security' 카테고리의 다른 글
Tomcat Executor (0) | 2024.10.29 |
---|---|
SecurityContext, SecurityContextHolder, 스레드 처리 방식 (1) | 2024.10.29 |
JSON Web Token (0) | 2024.10.28 |
SHA-256 (4) | 2024.10.28 |
자체 서명된 인증서 생성하기 (1) | 2024.10.28 |