본문 바로가기
JAVA/SpringBoot

SpringBoot 사용자 인증 구현: 인증 과정과 BCryptPasswordEncoder 활용

by GangDev 2024. 3. 26.

 

인증은 사용자가 누구인지 확인하는 과정인데, SpringBoot 에서는 인증을 구현하기 위해 여러 가지 방법을 제공한다.
(사용자 이름과 비밀번호를 통한 인증, 커스텀 사용자 서비스 구현, AuthenticationManager 를 사용한 인증 프로세스 관리, BCryptPasswordEncoder 를 사용한 패스워드 인코딩 등)

 

사용자 이름과 비밀번호를 통한 인증 >>
사용자 이름과 비밀번호를 통한 인증 과정은 UserDetailsService 인터페이스의 구현을 통해 이루어진다.
UserDetailsService는 사용자의 세부 정보를 로딩하는 데 사용되는 인터페이스이며, 사용자 이름을 기반으로 사용자 정보를 조회하고, Spring Security 형태에 맞춰 데이터를 변환할 수 있다.

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));

        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());
    }
}

UserRepository 는 JPA 리포지토리 인터페이스로, 데이터베이스에서 사용자 정보를 조회하는 데 사용된다.
findByUsername 메소드는 사용자 이름을 기반으로 데이터베이스에서 사용자를 찾는 데 사용된다.

 

loadUserByUsername 메소드는 사용자 이름을 매개변수로 받는다.
UserRepository를 사용하여 해당 사용자 이름에 해당하는 사용자를 찾는다.
사용자를 찾지 못한 경우 exception을 발생시킨다(UsernameNotFoundException)
사용자 정보를 기반으로 UserDetails 구현체를 생성하여 반환한다(org.springframework.security.core.userdetails.User)

 

userDetails 객체는 Spring Security 에서 사용자의 정보를 담는 인터페이스이다.
이 객체는 사용자 이름, 패스워드, 권한 목록, 계정 활성 여부, 만료 여부, 잠겨있는지 등의 상태 정보를 포함한다.
위 코드에서는 사용자 이름과 비밀번호만을 UserDetails 객체에 설정했다.


AuthenticationManager 는 Spring Security 에서 인증(AUthentication) 프로세스를 관리하는 인터페이스이며, 사용자 인증 로직을 구성하고 관리할 수 있다.
AuthenticationManager 는 주로 AuthenticationManagerBuilder 를 사용하여 구성되며, 이를 통해 사용자 서비스와 패스워드 인코더 등을 설정한다.

 

Authentication authenticate(Authentication authentication) throws AuthenticationException
이 메소드는 AUthentication 객체를 받아 인증을 수행하고, 성공적으로 인증된 Authentication 객체를 반환한다.
인증에 실패 시, AuthenticationException 을 발생시킨다.

 

AuthenticationManagerBuilder 는 AuthenticationManager 인스턴스를 구성하기 위한 빌더 클래스이며, 이를 사용하여 인증 메커니즘을 커스터마이즈할 수 있다.
(예시: auth.inMemoryAuthentication().withUser("user").password("password").roles("USER"))

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder);
    }
}

auth.userDetailsService(userDetailsService) 는 사용자 정보를 로드하는 서비스다>
CustomUserDetailsService 와 같은 구현체가 여기에 주입된다.

 

auth.passwordEncoder(passwordEncoder) 를 사용하여 패스워드를 인코딩/디코딩하는 데 사용할 PasswordEncoder를 설정한다.
(예를 들어, BCryptPasswordEncoder 가 사용될 수 있다)

 

configure 메소드는 AuthenticationManagerBuilder 를 사용하여 사용자 서비스와 패스워드 인코더를 설정하는 곳이다.


BCryptPasswordEncoder 는 SpringSecurity 에서 제공하는 패스워드 인코딩을 위한 클래스 중 하나로, BCrypt 해시 함수를 사용하여 패스워드를 암호화한다.
패스워드를 일반 텍스트로 저장하는 것은 보안상 매우 위험하기 때문에, BCryptPasswordEncoder 를 사용하여 데이터베이스에 저장하기 전에 패스워드를 안전하게 인코딩해야 한다.

 

BCryptPasswordEncoder의 기능 >>
사용자의 원본 패스워드를 받아 안전한 해시로 변환한다.
BCrypt는 솔트를 사용하여 해시를 생성하고, 브루트 포스 공격에 대한 저항력이 있는 방식으로 작동한다.
사용자가 입력한 패스워드를 인코딩할 뿐만 아니라, 로그인 시 제출된 패스워드를 데이터베이스의 해시된 패스워드와 비교하는 기능도 제공한다.

 

사용법 >>

 

먼저 BCryptPasswordEncoder 를 스프링 빈으로 등록해야 한다.
@Bean 어노테이션을 사용하여 BCryptPasswordEncoder 의 인스턴스를 생성하고 스프링 컨테이너에 등록한다.

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

 

사용자가 패스워드를 설정하거나 변경할 때, 패스워드를 BCyptPasswordEncoder 를 사용하여 인코딩한다.
인코딩된 패스워드는 데이터베이스에 저장된다.

@Autowired
private PasswordEncoder passwordEncoder;

public void createUser(String username, String rawPassword) {
    String encodedPassword = passwordEncoder.encode(rawPassword);
    // 사용자 객체 생성 및 패스워드 설정
    User user = new User(username, encodedPassword);
    // 사용자 객체를 데이터베이스에 저장
    userRepository.save(user);
}

 

로그인 과정에서 사용자가 제출한 패스워드를 검증할 때, BCryptPasswordEncoder 의 matches 메소드를 사용하여 제출된 패스워드와 데이터베이스에 저장된 해시된 패스워드를 비교한다.

public boolean checkPassword(String rawPassword, String storedEncodedPassword) {
    return passwordEncoder.matches(rawPassword, storedEncodedPassword);
}

 

---

24년 1월 23일 작성된 글입니다