인증은 사용자가 누구인지 확인하는 과정인데, 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일 작성된 글입니다
'JAVA > SpringBoot' 카테고리의 다른 글
Spring Boot 서버 포트 충돌 문제 해결 방법 : 로그 확인, 포트 사용 확인, 애플리케이션 설정 확인 등 (0) | 2024.04.20 |
---|---|
Spring Boot 애플리케이션 시작 실패 원인 분석: FailureAnalyzer 인터페이스 활용 (0) | 2024.04.20 |
타임리프의 인라인 표현식과 국제화:동적 웹 개발과 다국어 지원 (0) | 2024.03.26 |
타임리프를 활용한 웹 페이지 레이아웃과 디자인 (0) | 2024.03.26 |
스프링부트 + 타임리프: 폼 바인딩과 유효성 검사 (0) | 2024.03.26 |