11.2 BCrypt 암호화 및 UserDetailsService 구현
회원 정보가 데이터베이스에 저장될 때 비밀번호는 절대 평문으로 저장되어서는 안 됩니다. 또한, 스프링 시큐리티가 DB에 있는 사용자 정보를 읽어올 수 있도록 다리를 놓아주어야 합니다.
1. PasswordEncoder와 BCrypt
BCrypt는 비밀번호 해싱에 널리 사용되는 알고리즘으로, 강력한 보안성과 함께 솔트(Salt) 를 자동으로 생성하여 레인보우 테이블 공격을 방어합니다.
PasswordEncoder 빈 등록
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
2. UserDetails와 UserDetailsService
스프링 시큐리티가 사용자 정보를 이해할 수 있도록 인터페이스를 구현해야 합니다.
- UserDetails: 사용자의 이름, 비밀번호, 권한 등의 정보를 담는 객체입니다.
- UserDetailsService: 데이터베이스 등 저장소에서 사용자 정보를 조회하여
UserDetails를 반환하는 서비스입니다.
UserDetailsService 커스텀 구현 예시
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByEmail(username)
.map(user -> User.builder()
.username(user.getEmail())
.password(user.getPassword()) // DB에는 BCrypt로 암호화된 값 필수
.roles(user.getRole().name())
.build())
.orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다: " + username));
}
}
3. 회원가입 시 비밀번호 암호화
사용자 등록 로직에서 반드시 PasswordEncoder를 사용해야 합니다.
public void signup(UserDto dto) {
String encodedPassword = passwordEncoder.encode(dto.getPassword());
User user = User.builder()
.email(dto.getEmail())
.password(encodedPassword)
.role(Role.USER)
.build();
userRepository.save(user);
}
🎯 핵심 요점
- 비밀번호는 반드시 BCrypt 와 같은 단방향 해시 알고리즘으로 암호화해야 합니다.
- UserDetailsService 는 시큐리티와 DB 사이의 연결 고리 역할을 합니다.
loadUserByUsername메서드에서 사용자 존재 여부를 검증하고UserDetails객체를 반환합니다.